Speed up opening big projects with lazy folios

This commit is contained in:
Laurent Trinques
2026-10-13 11:01:21 +02:00
parent a78e95be2c
commit 77e2a9bfc5
45 changed files with 3956 additions and 388 deletions
+9
View File
@@ -1011,6 +1011,15 @@ bool TerminalStrip::fromXml(QDomElement &xml_element)
auto xml_layout = xml_element.firstChildElement(QStringLiteral("layout"));
if (!xml_layout.isNull())
{
//The folios holding its terminals are built (QET_LAZY_FOLIOS):
//a strip is read with the project, before the database could
//tell where any symbol is
QSet<QUuid> terminals;
const QDomNodeList xml_reals = xml_layout.elementsByTagName(RealTerminal::xmlTagName());
for (int i = 0 ; i < xml_reals.size() ; ++i)
terminals.insert(QUuid(xml_reals.at(i).toElement().attribute(QStringLiteral("element_uuid"))));
m_project->buildFoliosHoldingSymbols(terminals);
//Get all free elements terminal of the project
const ElementProvider ep(m_project);
auto free_terminals = ep.freeTerminal();
+5 -2
View File
@@ -105,7 +105,9 @@ QList<Diagram *> AutoNumSchemeCommand::usersOf(const QETProject *project, Kind k
if (!project || title.isEmpty()) {
return users;
}
for (Diagram *diagram : project->diagrams()) {
//The schemes a folio uses are its own data, read on opening:
//folios() builds nothing
for (Diagram *diagram : project->folios()) {
if (kind == Kind::Conductor) {
if (diagram->conductorsAutonumName() == title) {
users << diagram;
@@ -234,7 +236,8 @@ void AutoNumSchemeCommand::rename(const QString &from, const QString &to)
if (m_project->conductorCurrentAutoNum() == from) {
m_project->setCurrentConductorAutoNum(to);
}
for (Diagram *d : m_project->diagrams()) {
//Folios' own data, read on opening: folios() builds nothing
for (Diagram *d : m_project->folios()) {
//The highest folio number given, kept by scheme title
for (auto *hash : {&d->m_cnd_unitfolio_max, &d->m_cnd_tenfolio_max, &d->m_cnd_hundredfolio_max}) {
if (hash->contains(from)) {
+37 -30
View File
@@ -369,10 +369,19 @@ ElementAutoNumSchemeCommand::NumberSupport ElementAutoNumSchemeCommand::numberSu
std::optional<int> ElementAutoNumSchemeCommand::numberOf(const NumberSupport &support,
const Element *element)
{
if (!support.supported || !element) {
if (!element) {
return std::nullopt;
}
return numberOf(support, element->sequenceStruct());
}
/// @return the number the sequential numbers @p seq give, if the scheme knows it
std::optional<int> ElementAutoNumSchemeCommand::numberOf(const NumberSupport &support,
const autonum::sequentialNumbers &seq)
{
if (!support.supported) {
return std::nullopt;
}
const autonum::sequentialNumbers seq = element->sequenceStruct();
const QStringList &list = support.type == QLatin1String("unit") ? seq.unit
: support.type == QLatin1String("ten") ? seq.ten
: seq.hundred;
@@ -385,14 +394,18 @@ std::optional<int> ElementAutoNumSchemeCommand::numberOf(const NumberSupport &su
}
namespace {
/// The elements of @p project which follow the scheme @p title and are not slaves or reports
QVector<Element *> numberedFollowers(const QETProject *project, const QString &title)
/// The symbols of @p project which follow the scheme @p title and are not
/// slaves or reports, built or not (QETProject::symbolNumbering())
QList<QETProject::SymbolNumbering> numberedFollowers(const QETProject *project, const QString &title)
{
QVector<Element *> list;
const auto followers = project->elementsUsingElementAutoNum(title);
for (Element *el : followers) {
if (el->linkType() != Element::Slave && !(el->linkType() & Element::AllReport)) {
list << el;
QList<QETProject::SymbolNumbering> list;
const QUuid id = project->elementAutoNumId(title);
if (id.isNull()) {
return list;
}
for (const auto &symbol : project->symbolNumbering()) {
if (symbol.numbered && !symbol.formula.isEmpty() && symbol.scheme == id) {
list << symbol;
}
}
return list;
@@ -411,13 +424,10 @@ QSet<QString> ElementAutoNumSchemeCommand::labelsHeldBesides(const QETProject *p
if (!project) {
return labels;
}
for (Diagram *d : project->diagrams()) {
for (QGraphicsItem *item : d->items()) {
if (auto *el = qgraphicsitem_cast<Element *>(item)) {
if (el != element) {
labels << el->elementInformations().value(QETInformation::ELMT_LABEL).toString();
}
}
//Folios not built yet answer from what building them would give
for (const auto &symbol : project->symbolNumbering()) {
if (symbol.element != element) {
labels << symbol.label;
}
}
labels.remove(QString());
@@ -453,8 +463,8 @@ std::optional<ElementAutoNumSchemeCommand::CounterConflict> ElementAutoNumScheme
CounterConflict conflict;
conflict.counter = counter;
for (const Element *el : numberedFollowers(project, title)) {
if (const auto number = numberOf(support, el)) {
for (const auto &symbol : numberedFollowers(project, title)) {
if (const auto number = numberOf(support, symbol.sequence)) {
if (*number >= counter) {
++conflict.count;
conflict.highest = std::max(conflict.highest, *number);
@@ -486,8 +496,8 @@ QList<ElementAutoNumSchemeCommand::GapRange> ElementAutoNumSchemeCommand::gapRan
return ranges;
}
QSet<int> used;
for (const Element *el : numberedFollowers(project, title)) {
if (const auto number = numberOf(support, el)) {
for (const auto &symbol : numberedFollowers(project, title)) {
if (const auto number = numberOf(support, symbol.sequence)) {
used << *number;
}
}
@@ -559,22 +569,19 @@ QList<int> ElementAutoNumSchemeCommand::freeNumbers(const QETProject *project,
QSet<int> used;
int highest = context.itemAt(support.partIndex).value(1).toInt();
for (const Element *el : numberedFollowers(project, title)) {
if (el == element) continue;
if (const auto number = numberOf(support, el)) {
for (const auto &symbol : numberedFollowers(project, title)) {
if (symbol.element == element) continue;
if (const auto number = numberOf(support, symbol.sequence)) {
used << *number;
highest = std::max(highest, *number);
}
}
QSet<QString> labels;
for (Diagram *d : project->diagrams()) {
for (QGraphicsItem *item : d->items()) {
if (auto *el = qgraphicsitem_cast<Element *>(item)) {
if (el != element) {
labels << el->elementInformations().value(QETInformation::ELMT_LABEL).toString();
}
}
//Folios not built yet answer from what building them would give
for (const auto &symbol : project->symbolNumbering()) {
if (symbol.element != element) {
labels << symbol.label;
}
}
labels.remove(QString());
@@ -18,6 +18,7 @@
#ifndef ELEMENTAUTONUMSCHEMECOMMAND_H
#define ELEMENTAUTONUMSCHEMECOMMAND_H
#include "assignvariables.h"
#include "numerotationcontext.h"
#include "renumberelementscommand.h"
@@ -201,6 +202,8 @@ class ElementAutoNumSchemeCommand : public QUndoCommand
static QSet<QString> labelsHeldBesides(const QETProject *project, const Element *element);
static NumberSupport numberSupport(const NumerotationContext &context);
static std::optional<int> numberOf(const NumberSupport &support, const Element *element);
static std::optional<int> numberOf(const NumberSupport &support,
const autonum::sequentialNumbers &seq);
static QList<GapRange> gapRanges(const QETProject *project, const QString &title);
static QList<int> freeNumbers(const QETProject *project, const QString &title,
const Element *element, int limit = 500);
@@ -175,7 +175,8 @@ void AutoNumberingManagementW::on_m_apply_folios_rb_clicked()
if (ui->m_from_folios_cb->count()<=0) {
ui->m_from_folios_cb->clear();
ui->m_from_folios_cb->addItem("");
foreach (Diagram *diagram, project_->diagrams()){
//Titles and positions only: folios() builds nothing
foreach (Diagram *diagram, project_->folios()){
if (diagram->title() != "")
ui->m_from_folios_cb->addItem(
diagram->title(),
@@ -206,16 +207,17 @@ void AutoNumberingManagementW::on_m_from_folios_cb_currentIndexChanged(
if (index > 0) {
ui->m_to_folios_cb->setEnabled(true);
ui->m_to_folios_cb->addItem("");
for (int i=index;i<project_->diagrams().size();i++) {
if (project_->diagrams().at(i)->title() != "") {
const QList<Diagram *> folios = project_->folios();
for (int i=index;i<folios.size();i++) {
if (folios.at(i)->title() != "") {
ui->m_to_folios_cb->addItem(
project_->diagrams().at(i)->title(),
project_->diagrams().at(i)->folioIndex());
folios.at(i)->title(),
folios.at(i)->folioIndex());
}
else ui->m_to_folios_cb->addItem(
QString::number(
project_->diagrams().at(i)->folioIndex()),
project_->diagrams().at(i)->folioIndex());
folios.at(i)->folioIndex()),
folios.at(i)->folioIndex());
}
applyEnable(true);
ui->m_selected_folios_le->clear();
+5 -4
View File
@@ -136,7 +136,8 @@ void FolioAutonumberingW::on_m_autonumber_tabs_rb_clicked()
if (ui->m_from_cb->count()<=0){
ui->m_from_cb->clear();
ui->m_from_cb->addItem("");
foreach (Diagram *diagram, project_->diagrams()){
//Titles only: folios() builds nothing
foreach (Diagram *diagram, project_->folios()){
ui->m_from_cb->addItem(diagram->title());
}
}
@@ -164,9 +165,9 @@ void FolioAutonumberingW::on_m_from_cb_currentIndexChanged(int)
ui->m_to_cb->clear();
if (index > 0){
ui->m_to_cb->setEnabled(true);
for (int i=index;i<project_->diagrams().size();i++)
ui->m_to_cb->addItem(project_
->diagrams().at(i)->title());
const QList<Diagram *> folios = project_->folios();
for (int i=index;i<folios.size();i++)
ui->m_to_cb->addItem(folios.at(i)->title());
applyEnable(true);
}
else{
+113 -23
View File
@@ -29,6 +29,10 @@
#include "titleblocktemplaterenderer.h"
#include "diagram.h"
#include "qetproject.h"
#include "dataBase/projectdatabase.h"
#include <QLocale>
#include <QPainter>
#include <QRegularExpression>
@@ -282,28 +286,98 @@ void BorderTitleBlock::borderFromXml(const QDomElement &xml_elmt) {
@return the properties of the titleblock
\~French les proprietes du cartouches
*/
TitleBlockProperties BorderTitleBlock::exportTitleBlock()
TitleBlockProperties BorderTitleBlock::exportTitleBlock() const
{
return current();
}
/**
@brief BorderTitleBlock::localTitleBlock
@return the properties of the titleblock this object holds itself,
whether or not its folio is placed in a project
*/
TitleBlockProperties BorderTitleBlock::localTitleBlock() const
{
TitleBlockProperties ip;
ip.author = author();
ip.date = date();
ip.title = title();
ip.filename = fileName();
ip.plant = plant();
ip.locmach = locmach();
ip.indexrev = indexrev();
ip.version = version();
ip.folio = folio();
ip.author = btb_author_;
ip.date = btb_date_;
ip.title = btb_title_;
ip.filename = btb_filename_;
ip.plant = btb_plant_;
ip.locmach = btb_locmach_;
ip.indexrev = btb_indexrev_;
ip.version = btb_version_;
ip.folio = btb_folio_;
ip.template_name = titleBlockTemplateName();
ip.display_at = m_edge;
ip.auto_page_num = autoPageNum();
ip.auto_page_num = btb_auto_page_num_;
ip.context = additional_fields_;
ip.collection = QET::QetCollection::Embedded;
return(ip);
}
/**
@brief BorderTitleBlock::current
@return the properties of the titleblock: for a folio placed in a
project the project's store holds them (DB-ACCESSORS-PLAN.md stage
4.4), for any other this object's own copy.
*/
TitleBlockProperties BorderTitleBlock::current() const
{
if (const TitleBlockProperties *s = stored())
return *s;
return localTitleBlock();
}
/**
@brief BorderTitleBlock::stored
@return the properties the project's store holds for this titleblock's
folio, or nullptr if it is not placed. Read at once: the getters use it
field by field, as label formulas ask for the folio and title of every
symbol's folio, and copying the whole properties each time costs.
*/
const TitleBlockProperties *BorderTitleBlock::stored() const
{
if (projectDataBase *store = placedStore())
return store->storedFolioTitleBlock(m_owner_folio->uuid());
return nullptr;
}
/**
@brief BorderTitleBlock::setOwnerFolio
@param folio : the folio this border and title block belongs to
*/
void BorderTitleBlock::setOwnerFolio(Diagram *folio)
{
m_owner_folio = folio;
}
/**
@brief BorderTitleBlock::placedStore
@return the project database holding this titleblock's properties, or
nullptr if its folio is not placed in a project
*/
projectDataBase *BorderTitleBlock::placedStore() const
{
QETProject *project = m_owner_folio ? m_owner_folio->project() : nullptr;
projectDataBase *store = project ? project->dataBase() : nullptr;
return store && store->placedFolio(m_owner_folio->uuid()) == m_owner_folio
? store : nullptr;
}
/**
@brief BorderTitleBlock::storeTitleBlock
Write this titleblock's properties to its project's store, once its
folio is placed: every change of them comes through here.
*/
void BorderTitleBlock::storeTitleBlock()
{
if (projectDataBase *store = placedStore())
store->storeFolioTitleBlock(m_owner_folio->uuid(), localTitleBlock());
}
/**
@brief BorderTitleBlock::importTitleBlock
@param ip the new properties of titleblock
@@ -333,6 +407,7 @@ void BorderTitleBlock::importTitleBlock(const TitleBlockProperties &ip)
// through setFolioData(),
// which in turn calls updateDiagramContextForTitleBlock().
emit(needTitleBlockTemplate(ip.template_name));
storeTitleBlock();
emit informationChanged();
}
@@ -391,6 +466,7 @@ void BorderTitleBlock::setTitleBlockTemplate(
const TitleBlockTemplate *titleblock_template) {
m_titleblock_template_renderer -> setTitleBlockTemplate(
titleblock_template);
storeTitleBlock();
}
/**
@@ -889,9 +965,21 @@ DiagramPosition BorderTitleBlock::convertPosition(const QPointF &pos)
void BorderTitleBlock::setFolio(const QString &folio)
{
btb_folio_ = folio;
storeTitleBlock();
emit (titleBlockFolioChanged(folio));
}
/**
@brief BorderTitleBlock::setAutoPageNum
@param title : the title of the folio numbering this folio follows,
nothing else changes
*/
void BorderTitleBlock::setAutoPageNum(const QString &title)
{
btb_auto_page_num_ = title;
storeTitleBlock();
}
/**
@brief BorderTitleBlock::updateDiagramContextForTitleBlock
Update the informations given to the titleblock template
@@ -915,9 +1003,10 @@ void BorderTitleBlock::updateDiagramContextForTitleBlock(
// So an empty page-level value is skipped only when a real project-level
// one is already there to show through; otherwise it still goes in
// empty, which is what makes the placeholder resolve to nothing.
const TitleBlockProperties tb = current();
DiagramContext context = initial_context;
foreach (QString key, additional_fields_.keys()) {
const QVariant value = additional_fields_[key];
foreach (QString key, tb.context.keys()) {
const QVariant value = tb.context[key];
if (!value.toString().isEmpty() || !context.contains(key))
context.addValue(key, value);
}
@@ -925,20 +1014,20 @@ void BorderTitleBlock::updateDiagramContextForTitleBlock(
// ... overridden by the historical and/or dynamically generated fields
QLocale var;
var.dateFormat(QLocale::ShortFormat);
context.addValue("author", btb_author_);
context.addValue("author", tb.author);
context.addValue(
"date",
QLocale::system().toString(btb_date_, QLocale::ShortFormat));
context.addValue("title", btb_title_);
context.addValue("filename", btb_filename_);
context.addValue("plant", btb_plant_);
context.addValue("locmach", btb_locmach_);
context.addValue("indexrev", btb_indexrev_);
context.addValue("version", btb_version_);
QLocale::system().toString(tb.date, QLocale::ShortFormat));
context.addValue("title", tb.title);
context.addValue("filename", tb.filename);
context.addValue("plant", tb.plant);
context.addValue("locmach", tb.locmach);
context.addValue("indexrev", tb.indexrev);
context.addValue("version", tb.version);
context.addValue("folio", btb_final_folio_);
context.addValue("folio-id", folio_index_);
context.addValue("folio-total", folio_total_);
context.addValue("auto_page_num", btb_auto_page_num_);
context.addValue("auto_page_num", tb.auto_page_num);
context.addValue("previous-folio-num", m_previous_folio_num);
context.addValue("next-folio-num", m_next_folio_num);
@@ -1041,11 +1130,12 @@ void BorderTitleBlock::setFolioData(
// regenerate the content of the folio field
// regenere le contenu du champ folio
btb_final_folio_ = btb_folio_;
btb_final_folio_ = folio();
if (btb_final_folio_.contains("%autonum")){
btb_final_folio_.replace("%autonum", autonum);
btb_folio_ = btb_final_folio_;
storeTitleBlock();
}
btb_final_folio_.replace("%id", QString::number(folio_index_));
btb_final_folio_.replace("%total", QString::number(folio_total_));
+25 -14
View File
@@ -29,6 +29,8 @@ class QPainter;
class DiagramPosition;
class TitleBlockTemplate;
class TitleBlockTemplateRenderer;
class Diagram;
class projectDataBase;
/**
@brief The BorderTitleBlock class
This class represents the border and the titleblock which frame a
@@ -107,34 +109,36 @@ class BorderTitleBlock : public QObject
QRectF outsideBorderRect() const;
QRectF insideBorderRect() const;
// methods to get title block basic data
// methods to get title block basic data. A folio placed in a
// project reads them from the project's store
// (DB-ACCESSORS-PLAN.md stage 4.4), any other from its own copy.
/// @return the value of the title block "Author" field
QString author() const { return(btb_author_); }
QString author() const { const TitleBlockProperties *s = stored(); return(s ? s->author : btb_author_); }
/// @return the value of the title block "Date" field
QDate date() const { return(btb_date_); }
QDate date() const { const TitleBlockProperties *s = stored(); return(s ? s->date : btb_date_); }
/// @return the value of the title block "Title" field
QString title() const { return(btb_title_); }
QString title() const { const TitleBlockProperties *s = stored(); return(s ? s->title : btb_title_); }
/// @return the value of the title block "Folio" field
QString folio() const { return(btb_folio_); }
QString folio() const { const TitleBlockProperties *s = stored(); return(s ? s->folio : btb_folio_); }
/// @return the value of the title block "Folio" field as displayed
QString finalfolio() const { return(btb_final_folio_); }
/// @return the value of the title block "Plant" field
QString plant() const { return(btb_plant_); }
QString plant() const { const TitleBlockProperties *s = stored(); return(s ? s->plant : btb_plant_); }
/// @return the value of the title block "Locmach" field
QString locmach() const { return(btb_locmach_); }
QString locmach() const { const TitleBlockProperties *s = stored(); return(s ? s->locmach : btb_locmach_); }
/// @return the value of the revision index block "Folio" field
QString indexrev() const { return(btb_indexrev_); }
QString indexrev() const { const TitleBlockProperties *s = stored(); return(s ? s->indexrev : btb_indexrev_); }
/// @return the value of the title block "File" field
QString fileName() const { return(btb_filename_); }
QString fileName() const { const TitleBlockProperties *s = stored(); return(s ? s->filename : btb_filename_); }
/// @return the value of the title block Additional Fields
QString version() const { return(btb_version_); }
QString version() const { const TitleBlockProperties *s = stored(); return(s ? s->version : btb_version_); }
/// @return the value of the title block Additional Fields
DiagramContext additionalFields() const {
return (additional_fields_); }
const TitleBlockProperties *s = stored(); return(s ? s->context : additional_fields_); }
/// @return the value of the title block
QString autoPageNum() const { return(btb_auto_page_num_); }
QString autoPageNum() const { const TitleBlockProperties *s = stored(); return(s ? s->auto_page_num : btb_auto_page_num_); }
/// The title of the folio numbering this folio follows, nothing else changes
void setAutoPageNum(const QString &title) { btb_auto_page_num_ = title; }
void setAutoPageNum(const QString &title);
/// @return the value of the total number of folios
int folioTotal() const { return(folio_total_);}
@@ -175,7 +179,9 @@ class BorderTitleBlock : public QObject
void borderToXml(QDomElement &);
void borderFromXml(const QDomElement &);
TitleBlockProperties exportTitleBlock();
TitleBlockProperties exportTitleBlock() const;
TitleBlockProperties localTitleBlock() const;
void setOwnerFolio(Diagram *folio);
void importTitleBlock(const TitleBlockProperties &);
BorderProperties exportBorder();
void importBorder(const BorderProperties &);
@@ -289,5 +295,10 @@ class BorderTitleBlock : public QObject
bool display_rows_ = false;
bool display_border_ = false;
TitleBlockTemplateRenderer *m_titleblock_template_renderer;
Diagram *m_owner_folio = nullptr;
TitleBlockProperties current() const;
const TitleBlockProperties *stored() const;
projectDataBase *placedStore() const;
void storeTitleBlock();
};
#endif
+6 -2
View File
@@ -335,6 +335,10 @@ void CableManager::refreshLabels(QETProject *project)
QString label;
};
//Without cables, the folios not built hold nothing this pass
//changes but stale references (QETProject::cableLabelFolios())
const QList<Diagram *> folios = project->cableLabelFolios();
QHash<QUuid, Binding> bindings;
for (Cable *cable : project->cables())
{
@@ -361,7 +365,7 @@ void CableManager::refreshLabels(QETProject *project)
//the core or the link does.
QHash<QUuid, Conductor *> conductors_by_uuid;
QHash<Element *, QList<Conductor *>> endings_on_report;
for (Diagram *diagram : project->diagrams())
for (Diagram *diagram : folios)
{
if (!diagram) continue;
for (Conductor *conductor : diagram->conductors())
@@ -440,7 +444,7 @@ void CableManager::refreshLabels(QETProject *project)
}
}
for (Diagram *diagram : project->diagrams())
for (Diagram *diagram : folios)
{
if (!diagram) continue;
for (Conductor *conductor : diagram->conductors())
+12 -4
View File
@@ -869,8 +869,8 @@ int exportLinks(QETProject &project, const QString &output)
/// diff can reveal markup QET silently normalises (tolerated-but-invalid XML).
int resaveProject(QETProject &project, const QString &output)
{
const QDomDocument doc = project.toXml();
if (QETProject::isContainerPath(output)) {
const QDomDocument doc = project.toXml();
QString error;
if (!QETProject::writeContainer(doc, output, &error)) {
err << "Cannot write '" << output << "': " << error << "\n";
@@ -879,13 +879,17 @@ int resaveProject(QETProject &project, const QString &output)
out << "Re-saved project -> " << output << "\n";
return 0;
}
//Folios not built (QET_LAZY_FOLIOS) written as read when that is
//what building them would write (QETProject::toXml())
QHash<int, QByteArray> kept_folios;
const QDomDocument doc = project.toXml(&kept_folios);
QFile file(output);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
err << "Cannot open '" << output << "' for writing.\n";
return 1;
}
QTextStream fout(&file);
fout << doc.toString(4);
fout << QETProject::spliceKeptFolios(doc.toString(4), kept_folios);
file.close();
out << "Re-saved project -> " << output << "\n";
return 0;
@@ -963,21 +967,25 @@ int setTitleBlock(QETProject &project, const QString &output,
++folios;
}
const QDomDocument doc = project.toXml();
if (QETProject::isContainerPath(output)) {
const QDomDocument doc = project.toXml();
QString error;
if (!QETProject::writeContainer(doc, output, &error)) {
err << "Cannot write '" << output << "': " << error << "\n";
return 1;
}
} else {
//Folios not built (QET_LAZY_FOLIOS) written as read when that is
//what building them would write (QETProject::toXml())
QHash<int, QByteArray> kept_folios;
const QDomDocument doc = project.toXml(&kept_folios);
QFile file(output);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
err << "Cannot open '" << output << "' for writing.\n";
return 1;
}
QTextStream fout(&file);
fout << doc.toString(4);
fout << QETProject::spliceKeptFolios(doc.toString(4), kept_folios);
file.close();
}
out << "Stamped " << fields.size() << " field(s) on "
+58 -30
View File
@@ -217,10 +217,22 @@ void SingleLineProperties::drawPen(QPainter *painter,
*/
void SingleLineProperties::toXml(QDomElement &e) const
{
e.setAttribute("ground", hasGround ? "true" : "false");
e.setAttribute("neutral", hasNeutral ? "true" : "false");
e.setAttribute("phase", phases);
if (isPen()) e.setAttribute("pen", "true");
for (const auto &attribute : attributes())
e.setAttribute(attribute.first, attribute.second);
}
/**
@brief SingleLineProperties::attributes
@return the attributes toXml() writes, in its order
*/
QList<QPair<QString, QString>> SingleLineProperties::attributes() const
{
QList<QPair<QString, QString>> a;
a << qMakePair(QStringLiteral("ground"), QString::fromLatin1(hasGround ? "true" : "false"));
a << qMakePair(QStringLiteral("neutral"), QString::fromLatin1(hasNeutral ? "true" : "false"));
a << qMakePair(QStringLiteral("phase"), QString::number(phases));
if (isPen()) a << qMakePair(QStringLiteral("pen"), QStringLiteral("true"));
return a;
}
/**
@@ -268,50 +280,66 @@ ConductorProperties::~ConductorProperties()
*/
void ConductorProperties::toXml(QDomElement &e) const
{
e.setAttribute("type", typeToString(type));
for (const auto &attribute : attributes())
e.setAttribute(attribute.first, attribute.second);
}
/**
@brief ConductorProperties::attributes
@return the attributes toXml() writes, in the order it writes them: the
project database stores a placed wire's properties as these.
*/
QList<QPair<QString, QString>> ConductorProperties::attributes() const
{
QList<QPair<QString, QString>> a;
auto add = [&a](const char *name, const QString &value) {
a << qMakePair(QString::fromLatin1(name), value);
};
add("type", typeToString(type));
if (color != QColor(Qt::black))
e.setAttribute("color", color.name());
add("color", color.name());
e.setAttribute("bicolor", m_bicolor? "true" : "false");
e.setAttribute("color2", m_color_2.name());
e.setAttribute("dash-size", QString::number(m_dash_size));
add("bicolor", m_bicolor? "true" : "false");
add("color2", m_color_2.name());
add("dash-size", QString::number(m_dash_size));
if (type == Single)
singleLineProperties.toXml(e);
a << singleLineProperties.attributes();
e.setAttribute("num", text);
e.setAttribute("text_color", text_color.name());
e.setAttribute("formula", m_formula);
e.setAttribute("cable", m_cable);
add("num", text);
add("text_color", text_color.name());
add("formula", m_formula);
add("cable", m_cable);
if (!m_cable_uuid.isNull())
{
//Only written when there is a cable: a conductor which is no
//part of one keeps whatever cable text it had and no reference
//at all, so a hand written value is never mistaken for a
//generated one.
e.setAttribute("cable_uuid", m_cable_uuid.toString());
e.setAttribute("cable_slot", QString::number(m_cable_slot));
add("cable_uuid", m_cable_uuid.toString());
add("cable_slot", QString::number(m_cable_slot));
}
e.setAttribute("bus", m_bus);
e.setAttribute("function", m_function);
e.setAttribute("tension_protocol", m_tension_protocol);
e.setAttribute("conductor_color", m_wire_color);
e.setAttribute("conductor_section", m_wire_section);
e.setAttribute("numsize", QString::number(text_size));
e.setAttribute("condsize", QString::number(cond_size));
e.setAttribute("displaytext", m_show_text);
e.setAttribute("onetextperfolio", m_one_text_per_folio);
e.setAttribute("vertirotatetext", QString::number(verti_rotate_text));
e.setAttribute("horizrotatetext", QString::number(horiz_rotate_text));
add("bus", m_bus);
add("function", m_function);
add("tension_protocol", m_tension_protocol);
add("conductor_color", m_wire_color);
add("conductor_section", m_wire_section);
add("numsize", QString::number(text_size));
add("condsize", QString::number(cond_size));
add("displaytext", QString::number(m_show_text));
add("onetextperfolio", QString::number(m_one_text_per_folio));
add("vertirotatetext", QString::number(verti_rotate_text));
add("horizrotatetext", QString::number(horiz_rotate_text));
QMetaEnum me = QMetaEnum::fromType<Qt::Alignment>();
e.setAttribute("horizontal-alignment", me.valueToKey(m_horizontal_alignment));
e.setAttribute("vertical-alignment", me.valueToKey(m_vertical_alignment));
add("horizontal-alignment", QString::fromLatin1(me.valueToKey(m_horizontal_alignment)));
add("vertical-alignment", QString::fromLatin1(me.valueToKey(m_vertical_alignment)));
QString conductor_style = writeStyle();
if (!conductor_style.isEmpty())
e.setAttribute("style", conductor_style);
add("style", conductor_style);
return a;
}
+2
View File
@@ -39,6 +39,7 @@ class SingleLineProperties {
bool isPen() const;
void draw(QPainter *, QET::ConductorSegmentType, const QRectF &);
void toXml(QDomElement &) const;
QList<QPair<QString, QString>> attributes() const;
void fromXml(QDomElement &);
void toSettings(QSettings &, const QString & = QString()) const;
void fromSettings(QSettings &, const QString & = QString());
@@ -130,6 +131,7 @@ class ConductorProperties
// methods
void toXml(QDomElement &) const;
QList<QPair<QString, QString>> attributes() const;
void fromXml(QDomElement &);
void toSettings(QSettings &, const QString & = QString()) const;
void fromSettings(QSettings &, const QString & = QString());
File diff suppressed because it is too large Load Diff
+114 -1
View File
@@ -24,9 +24,15 @@
#include <QPointer>
#include <QFileDialog>
#include <QHash>
#include <QMap>
#include <QDomDocument>
#include <QSet>
#include <QUuid>
#include "../diagramcontext.h"
#include "../titleblockproperties.h"
#include "../conductorproperties.h"
class Element;
class QETProject;
class Diagram;
@@ -98,6 +104,62 @@ class projectDataBase : public QObject
QSqlDatabase database() const {return m_data_base;}
int excludedConductorCount() const;
//The information of every symbol placed in the project, by
//symbol uuid: the store each symbol's information is written
//through (DB-ACCESSORS-PLAN.md stage 4.1). A symbol not placed
//(a preview, the symbol editor) keeps its own.
DiagramContext elementInformation(const QUuid &element) const;
bool hasElementInformation(const QUuid &element) const;
//How many placed symbols hold @p element: more than one in a
//file whose symbols were copied by an old version (F100), or
//for a moment while a copy's uuid is renewed
int placedElementCount(const QUuid &element) const;
bool storeElementInformation(const QUuid &element, const DiagramContext &information);
void elementInformationChanged(Element *element, const DiagramContext &information);
void forgetElementInformation(const QUuid &element);
void elementUuidChanged(Element *element, const QUuid &old_uuid);
//Placed symbols whose information differs from the store's,
//each as "uuid: why"; empty when they all agree
QStringList elementInformationMismatches() const;
//The title block properties of every folio of the project, by
//folio uuid: the store each folio's title block is written
//through (DB-ACCESSORS-PLAN.md stage 4.4). A folio not in a
//project keeps its own.
TitleBlockProperties folioTitleBlock(const QUuid &folio) const;
const TitleBlockProperties *storedFolioTitleBlock(const QUuid &folio) const;
Diagram *placedFolio(const QUuid &folio) const;
void storeFolioTitleBlock(const QUuid &folio, const TitleBlockProperties &properties);
void placeFolio(Diagram *folio);
void folioUuidChanged(Diagram *folio, const QUuid &old_uuid);
QStringList folioTitleBlockMismatches() const;
//The properties of every wire placed in the project, by wire
//uuid: the store each wire's properties are written through
//(DB-ACCESSORS-PLAN.md stage 4.5). A wire not placed keeps its own.
ConductorProperties conductorProperties(const QUuid &conductor) const;
bool hasConductorProperties(const QUuid &conductor) const;
int placedConductorCount(const QUuid &conductor) const;
void conductorPropertiesStored(Conductor *conductor, const ConductorProperties &properties);
void conductorUuidChanged(Conductor *conductor, const QUuid &old_uuid);
QStringList conductorPropertiesMismatches() const;
//Fill the three stores from a project's document before its
//folios are built (DB-ACCESSORS-PLAN.md stage 4.6), then, once
//they are, say how many of those values building agreed with.
void prefillFromDocument(const QDomDocument &document);
void endPrefill(bool keep_unplaced = false);
//A folio of a project opened with QET_LAZY_FOLIOS is being built:
//its rows came from the document already, so none are written
//and the database is not marked changed
void setBuildingFolio(bool building);
bool isBuildingFolio() const {return m_building_folios > 0;}
bool rowsFromDocument() const {return m_rows_from_document;}
QString prefillReport() const;
QSet<QUuid> foliosWithUnjoinedWires() const;
QSet<QUuid> folioUuidsOfElements(const QSet<QUuid> &elements) const;
QHash<QUuid, QString> elementTypesOnFolios(const QSet<QUuid> &folios) const;
void addElement (Element *element);
void removeElement (Element *element);
void elementInfoChanged (Element *element);
@@ -145,6 +207,7 @@ class projectDataBase : public QObject
void flushElementPositions();
void populateElementTable();
void populateElementInfoTable();
void populateElementInfoTableFromFolios();
void populateDiagramInfoTable();
void populateConductorTable();
void populateLinkTable();
@@ -203,7 +266,12 @@ class projectDataBase : public QObject
m_cascade_remove_element_query,
m_insert_shape_query,
m_insert_independent_text_query,
m_insert_image_query;
m_insert_image_query,
m_store_remove_query,
m_store_insert_query,
m_remove_links_query,
m_conductor_properties_insert_query,
m_conductor_property_remove_query;
//Which uuid's row each drawing item last wrote, and which item
//wrote each row. A pasted copy is added to the folio still
@@ -222,6 +290,51 @@ class projectDataBase : public QObject
//sends xChanged and yChanged for every step of a drag, so the
//cell is written when next read, as drawing items are.
QList<QPointer<Element>> m_moved_elements;
QHash<QUuid, DiagramContext> m_element_information;
//The placed symbols holding each uuid: a paste or a folio copy
//briefly gives a copy its original's uuid, so one uuid can have
//two symbols until the copy's is renewed.
QMultiHash<QUuid, QPointer<Element>> m_placed_elements;
QHash<QUuid, TitleBlockProperties> m_folio_titleblocks;
QHash<QUuid, QPointer<Diagram>> m_placed_folios;
QHash<QUuid, ConductorProperties> m_conductor_properties;
//The rows of conductor_properties, to write only what changes
QHash<QUuid, QHash<QString, QString>> m_conductor_attributes;
QSet<QUuid> m_dirty_conductor_properties;
int m_building_folios = 0;
QSet<QUuid> m_folios_with_unjoined_wires;
//The folios whose rows a rebuild keeps (keepUnbuiltFolioRows())
QSet<Diagram *> m_kept_folios;
//The tables' rows were filled from the document: those of a
//folio not built yet are right (QET_LAZY_FOLIOS)
bool m_rows_from_document = false;
void keepUnbuiltFolioRows();
void restoreKeptRows(const QString &table, Diagram *folio, const QString &where);
void dropKeptRows();
bool m_changed_before_folio = false, m_blocked_before_folio = false;
QHash<QUuid, DiagramContext> m_prefilled_elements;
QHash<QUuid, TitleBlockProperties> m_prefilled_folios;
QHash<QUuid, ConductorProperties> m_prefilled_conductors;
struct PrefillTally {
int agree = 0, differ = 0, unplaced = 0;
QMap<QString, int> fields;
};
PrefillTally m_prefill_tally[3];
void flushConductorProperties();
QMultiHash<QUuid, QPointer<Conductor>> m_placed_conductors;
bool storeConductorProperties(const QUuid &conductor, const ConductorProperties &properties);
void placeConductor(Conductor *conductor);
void unplaceConductor(Conductor *conductor, const QUuid &uuid);
Conductor *otherConductorHolder(const QUuid &uuid, const Conductor *conductor) const;
void writeConductorRow(Conductor *conductor);
void removeConductorRow(const QUuid &uuid);
void unplaceFolio(Diagram *folio, const QUuid &uuid);
void placeElement(Element *element);
Element *otherHolder(const QUuid &uuid, const Element *element) const;
void writeElementRows(Element *element);
void queueLinks(Element *element);
void removeElementRows(const QUuid &uuid);
void unplaceElement(Element *element, const QUuid &uuid);
#ifdef QET_EXPORT_PROJECT_DB
public:
+214 -119
View File
@@ -16,6 +16,7 @@
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "diagram.h"
#include "dataBase/projectdatabase.h"
#include "autoNum/elementautonumschemecommand.h"
#include "ElementsCollection/elementcollectionhandler.h"
@@ -193,6 +194,7 @@ Diagram::Diagram(QETProject *project) :
* http://tech-artists.org/t/qt-properly-removing-qgraphicitems/3063
*/
border_and_titleblock.setOwnerFolio(this);
qgi_manager_ = new QGIManager(this);
setBackgroundBrush(Qt::white);
conductor_setter_ = new QGraphicsLineItem(nullptr);
@@ -767,6 +769,49 @@ void Diagram::keyReleaseEvent(QKeyEvent *e)
}
}
/**
@brief Diagram::ensureLoaded
Build this folio's items from the XML kept when the project was
opened with QET_LAZY_FOLIOS, and do for them what opening does for
every folio's (QETProject::folioLoaded()). Nothing for a folio built
already.
*/
void Diagram::ensureLoaded()
{
if (m_deferred.isNull()) {
return;
}
if (m_project) {
m_project->buildFolios({this});
} else {
buildDeferred();
}
}
/**
@brief Diagram::buildDeferred
Build this folio's items from the XML kept for later, and nothing else.
Their rows are in the project database already, filled from the same
XML: building writes none.
@return true if there was something to build
*/
bool Diagram::buildDeferred()
{
if (m_deferred.isNull()) {
return false;
}
QDomElement xml = m_deferred;
m_deferred = QDomElement();
m_kept_text.clear();
projectDataBase *db = m_project ? m_project->dataBase() : nullptr;
if (db) db->setBuildingFolio(true);
m_loading_deferred = true;
fromXml(xml, QPointF(), true, nullptr);
m_loading_deferred = false;
if (db) db->setBuildingFolio(false);
return true;
}
/**
@brief Diagram::uuid
@return the uuid of this diagram
@@ -801,7 +846,7 @@ bool Diagram::uuidUsedByOtherDiagram(const QUuid &uuid) const
if (!m_project) {
return false;
}
const auto diagrams = m_project->diagrams();
const auto diagrams = m_project->folios(); //uuids only: no need to build them
for (const Diagram *diagram : diagrams) {
if (diagram != this && diagram->m_uuid == uuid) {
return true;
@@ -838,7 +883,7 @@ QUuid Diagram::derivedUuid(const QDomElement &root, const QString &reason) const
QStringLiteral("{d5951240-154d-44d6-8277-0092a31d1920}"));
const int index = m_project
? m_project->diagrams().indexOf(const_cast<Diagram *>(this))
? m_project->folios().indexOf(const_cast<Diagram *>(this))
: -1;
const QString project_title = root.ownerDocument()
.documentElement()
@@ -1088,6 +1133,10 @@ QList < QSet <Conductor *> > Diagram::potentials()
\~French Un Document XML (QDomDocument)
*/
QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
//A folio of a project opened with QET_LAZY_FOLIOS is written from
//its built items (QETProject::toXml() may write one not built as
//it was read instead)
ensureLoaded();
// document
QDomDocument document;
@@ -1098,120 +1147,7 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
// schema properties
// proprietes du schema
if (whole_content) {
//Persist the folio identity, so that a folio keeps the same uuid
//across save/load. Without it every load invents a new one, and
//nothing outside the running instance (version control, a lock,
//a diff tool...) can tell which folio is which.
dom_root.setAttribute(QStringLiteral("uuid"), m_uuid.toString());
border_and_titleblock.titleBlockToXml(dom_root);
border_and_titleblock.borderToXml(dom_root);
// Default conductor properties
QDomElement default_conductor =
document.createElement(QStringLiteral("defaultconductor"));
defaultConductorProperties.toXml(default_conductor);
dom_root.appendChild(default_conductor);
// Conductor autonum
if (!m_conductors_autonum_name.isEmpty()) {
dom_root.setAttribute(QStringLiteral("conductorAutonum"),
m_conductors_autonum_name);
}
//Default New Element
dom_root.setAttribute(QStringLiteral("freezeNewElement"),
m_freeze_new_elements ? QStringLiteral("true") : QStringLiteral("false"));
//Default New Conductor
dom_root.setAttribute(QStringLiteral("freezeNewConductor"),
m_freeze_new_conductors_
? QStringLiteral("true") : QStringLiteral("false"));
//Element Folio Sequential Variables
if (!m_elmt_unitfolio_max.isEmpty()
|| !m_elmt_tenfolio_max.isEmpty()
|| !m_elmt_hundredfolio_max.isEmpty()) {
QDomElement elmtfoliosequential =
document.createElement(
QStringLiteral("elementautonumfoliosequentials"));
if (!m_elmt_unitfolio_max.isEmpty()) {
QDomElement elmtfolioseq =
document.createElement(
QStringLiteral("elementunitfolioseq"));
folioSequentialsToXml(&m_elmt_unitfolio_max,
&elmtfolioseq,
QStringLiteral("sequf_"),
QStringLiteral("unitfolioseq"),
&document);
elmtfoliosequential.appendChild(elmtfolioseq);
}
if (!m_elmt_tenfolio_max.isEmpty()) {
QDomElement elmtfolioseq =
document.createElement(
QStringLiteral("elementtenfolioseq"));
folioSequentialsToXml(&m_elmt_tenfolio_max,
&elmtfolioseq,
QStringLiteral("seqtf_"),
QStringLiteral("tenfolioseq"),
&document);
elmtfoliosequential.appendChild(elmtfolioseq);
}
if (!m_elmt_hundredfolio_max.isEmpty()) {
QDomElement elmtfolioseq =
document.createElement(
QStringLiteral("elementhundredfolioseq"));
folioSequentialsToXml(&m_elmt_hundredfolio_max,
&elmtfolioseq,
QStringLiteral("seqhf_"),
QStringLiteral("hundredfolioseq"),
&document);
elmtfoliosequential.appendChild(elmtfolioseq);
}
dom_root.appendChild(elmtfoliosequential);
}
// Conductor Folio Sequential Variables
if (!m_cnd_unitfolio_max.isEmpty()
|| !m_cnd_tenfolio_max.isEmpty() ||
!m_cnd_hundredfolio_max.isEmpty()) {
QDomElement cndfoliosequential =
document.createElement(
QStringLiteral("conductorautonumfoliosequentials"));
QHash<QString, QStringList>::iterator i;
if (!m_cnd_unitfolio_max.isEmpty()) {
QDomElement cndfolioseq =
document.createElement(
QStringLiteral("conductorunitfolioseq"));
folioSequentialsToXml(&m_cnd_unitfolio_max,
&cndfolioseq,
QStringLiteral("sequf_"),
QStringLiteral("unitfolioseq"),
&document);
cndfoliosequential.appendChild(cndfolioseq);
}
if (!m_cnd_tenfolio_max.isEmpty()) {
QDomElement cndfolioseq =
document.createElement(
QStringLiteral("conductortenfolioseq"));
folioSequentialsToXml(&m_cnd_tenfolio_max,
&cndfolioseq,
QStringLiteral("seqtf_"),
QStringLiteral("tenfolioseq"),
&document);
cndfoliosequential.appendChild(cndfolioseq);
}
if (!m_cnd_hundredfolio_max.isEmpty()) {
QDomElement cndfolioseq =
document.createElement(
QStringLiteral("conductorhundredfolioseq"));
folioSequentialsToXml(&m_cnd_hundredfolio_max,
&cndfolioseq,
QStringLiteral("seqhf_"),
QStringLiteral("hundredfolioseq"),
&document);
cndfoliosequential.appendChild(cndfolioseq);
}
dom_root.appendChild(cndfoliosequential);
}
writeHeader(document, dom_root);
}
else {
//this method with whole_content to false,
@@ -1307,8 +1243,15 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
// since those ids are assigned sequentially in element order.
std::stable_sort(list_elements.begin(), list_elements.end(),
[](Element *a, Element *b) { return elementSortKey(a) < elementSortKey(b); });
//Wires that tie on their ends (common: several from one terminal)
//keep the order they were made in, which for a project's is its
//file's -- the order the scene gave them so far, except for a folio
//built late (QET_LAZY_FOLIOS), whose scene order differed
std::stable_sort(list_conductors.begin(), list_conductors.end(),
[](Conductor *a, Conductor *b) { return conductorSortKey(a) < conductorSortKey(b); });
[](Conductor *a, Conductor *b) {
const QString ka = conductorSortKey(a), kb = conductorSortKey(b);
return ka != kb ? ka < kb : a->creationRank() < b->creationRank();
});
// A copy carries the numberings its elements follow: pasted into
// another project, which does not know them, it can offer to import
@@ -1375,6 +1318,143 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
return(document);
}
/**
@brief Diagram::writeHeader
Write this folio's own data -- its uuid, title block, border, default
wire, numbering -- into @p dom_root, the folio's element of @p document:
what toXml() writes before the folio's items.
*/
void Diagram::writeHeader(QDomDocument &document, QDomElement &dom_root)
{
//Persist the folio identity, so that a folio keeps the same uuid
//across save/load. Without it every load invents a new one, and
//nothing outside the running instance (version control, a lock,
//a diff tool...) can tell which folio is which.
dom_root.setAttribute(QStringLiteral("uuid"), m_uuid.toString());
border_and_titleblock.titleBlockToXml(dom_root);
border_and_titleblock.borderToXml(dom_root);
// Default conductor properties
QDomElement default_conductor =
document.createElement(QStringLiteral("defaultconductor"));
defaultConductorProperties.toXml(default_conductor);
dom_root.appendChild(default_conductor);
// Conductor autonum
if (!m_conductors_autonum_name.isEmpty()) {
dom_root.setAttribute(QStringLiteral("conductorAutonum"),
m_conductors_autonum_name);
}
//Default New Element
dom_root.setAttribute(QStringLiteral("freezeNewElement"),
m_freeze_new_elements ? QStringLiteral("true") : QStringLiteral("false"));
//Default New Conductor
dom_root.setAttribute(QStringLiteral("freezeNewConductor"),
m_freeze_new_conductors_
? QStringLiteral("true") : QStringLiteral("false"));
//Element Folio Sequential Variables
if (!m_elmt_unitfolio_max.isEmpty()
|| !m_elmt_tenfolio_max.isEmpty()
|| !m_elmt_hundredfolio_max.isEmpty()) {
QDomElement elmtfoliosequential =
document.createElement(
QStringLiteral("elementautonumfoliosequentials"));
if (!m_elmt_unitfolio_max.isEmpty()) {
QDomElement elmtfolioseq =
document.createElement(
QStringLiteral("elementunitfolioseq"));
folioSequentialsToXml(&m_elmt_unitfolio_max,
&elmtfolioseq,
QStringLiteral("sequf_"),
QStringLiteral("unitfolioseq"),
&document);
elmtfoliosequential.appendChild(elmtfolioseq);
}
if (!m_elmt_tenfolio_max.isEmpty()) {
QDomElement elmtfolioseq =
document.createElement(
QStringLiteral("elementtenfolioseq"));
folioSequentialsToXml(&m_elmt_tenfolio_max,
&elmtfolioseq,
QStringLiteral("seqtf_"),
QStringLiteral("tenfolioseq"),
&document);
elmtfoliosequential.appendChild(elmtfolioseq);
}
if (!m_elmt_hundredfolio_max.isEmpty()) {
QDomElement elmtfolioseq =
document.createElement(
QStringLiteral("elementhundredfolioseq"));
folioSequentialsToXml(&m_elmt_hundredfolio_max,
&elmtfolioseq,
QStringLiteral("seqhf_"),
QStringLiteral("hundredfolioseq"),
&document);
elmtfoliosequential.appendChild(elmtfolioseq);
}
dom_root.appendChild(elmtfoliosequential);
}
// Conductor Folio Sequential Variables
if (!m_cnd_unitfolio_max.isEmpty()
|| !m_cnd_tenfolio_max.isEmpty() ||
!m_cnd_hundredfolio_max.isEmpty()) {
QDomElement cndfoliosequential =
document.createElement(
QStringLiteral("conductorautonumfoliosequentials"));
QHash<QString, QStringList>::iterator i;
if (!m_cnd_unitfolio_max.isEmpty()) {
QDomElement cndfolioseq =
document.createElement(
QStringLiteral("conductorunitfolioseq"));
folioSequentialsToXml(&m_cnd_unitfolio_max,
&cndfolioseq,
QStringLiteral("sequf_"),
QStringLiteral("unitfolioseq"),
&document);
cndfoliosequential.appendChild(cndfolioseq);
}
if (!m_cnd_tenfolio_max.isEmpty()) {
QDomElement cndfolioseq =
document.createElement(
QStringLiteral("conductortenfolioseq"));
folioSequentialsToXml(&m_cnd_tenfolio_max,
&cndfolioseq,
QStringLiteral("seqtf_"),
QStringLiteral("tenfolioseq"),
&document);
cndfoliosequential.appendChild(cndfolioseq);
}
if (!m_cnd_hundredfolio_max.isEmpty()) {
QDomElement cndfolioseq =
document.createElement(
QStringLiteral("conductorhundredfolioseq"));
folioSequentialsToXml(&m_cnd_hundredfolio_max,
&cndfolioseq,
QStringLiteral("seqhf_"),
QStringLiteral("hundredfolioseq"),
&document);
cndfoliosequential.appendChild(cndfolioseq);
}
dom_root.appendChild(cndfoliosequential);
}
}
/**
@brief Diagram::headerXml
@return the folio's own data as writeHeader() writes it, as text
*/
QString Diagram::headerXml()
{
QDomDocument document;
auto dom_root = document.createElement(QStringLiteral("diagram"));
document.appendChild(dom_root);
writeHeader(document, dom_root);
return document.toString();
}
/**
@brief Diagram::folioSequentialsToXml
Add folio sequential to QDomElement
@@ -1692,8 +1772,9 @@ bool Diagram::fromXml(QDomElement &document,
return(false);
}
// Read attributes of this diagram
if (consider_informations)
// Read attributes of this diagram (already read for a folio whose
// items are built later, see ensureLoaded())
if (consider_informations && !m_loading_deferred)
{
// Restore the persisted folio uuid. Done first, before any item is
// loaded, so that everything created below sees the final uuid.
@@ -1701,6 +1782,7 @@ bool Diagram::fromXml(QDomElement &document,
// persisted, or uuid already taken by another folio) gets a
// deterministic one instead, see derivedUuid().
const QUuid persisted_uuid(root.attribute(QStringLiteral("uuid")));
const QUuid old_uuid = m_uuid;
if (persisted_uuid.isNull()) {
m_uuid = derivedUuid(root, QStringLiteral("legacy"));
} else if (uuidUsedByOtherDiagram(persisted_uuid)) {
@@ -1709,6 +1791,9 @@ bool Diagram::fromXml(QDomElement &document,
} else {
m_uuid = persisted_uuid;
}
//A placed folio's stored title block follows its uuid
if (m_project && m_project->dataBase())
m_project->dataBase()->folioUuidChanged(this, old_uuid);
// Load border and titleblock
border_and_titleblock.titleBlockFromXml(root);
@@ -1776,6 +1861,16 @@ bool Diagram::fromXml(QDomElement &document,
return(true);
}
//A project opened with QET_LAZY_FOLIOS builds a folio's items when
//they are first needed: until then the folio has what it says
//about itself (uuid, title block, border, numbering) and keeps its
//XML, which belongs to the project's document.
if (consider_informations && !m_loading_deferred && m_project
&& m_project->deferringFolios()) {
m_deferred = root;
return(true);
}
//If paste from another project
if (root.hasAttribute(QStringLiteral("projectId")))
{
+21
View File
@@ -140,6 +140,11 @@ class Diagram : public QGraphicsScene
bool m_freeze_new_elements;
bool m_freeze_new_conductors_;
QUuid m_uuid = QUuid::createUuid();
/// The XML of this folio's items, while they are not built yet
QDomElement m_deferred;
QString m_opened_header;
QByteArray m_kept_text;
bool m_loading_deferred = false;
//Selection before the current click, see completeGroupSelection()
QList<QPointer<QGraphicsObject>> m_previous_selection;
@@ -200,10 +205,26 @@ class Diagram : public QGraphicsScene
// methods related to XML import/export
QDomDocument toXml(bool wholeContent = true, bool is_copy_command = false);
void writeHeader(QDomDocument &document, QDomElement &dom_root);
bool initFromXml(QDomElement &,
QPointF = QPointF(),
bool = true,
DiagramContent * = nullptr);
/// false while this folio's items wait in its XML to be
/// built (QET_LAZY_FOLIOS, LAZY-FOLIO-PLAN.md)
bool isLoaded() const {return m_deferred.isNull();}
QDomElement deferredXml() const {return m_deferred;}
QString headerXml();
/// The folio's text in the file it was read from, while it is
/// not built (QETProject::toXml())
QByteArray keptText() const {return m_kept_text;}
void setKeptText(const QByteArray &text) {m_kept_text = text;}
/// Remember the folio's own data as it is now, for
/// headerChanged() (QETProject::toXml())
void rememberHeader() {m_opened_header = headerXml();}
bool headerChanged() {return headerXml() != m_opened_header;}
void ensureLoaded();
bool buildDeferred();
//The last argument is only for a copy fragment: when it is
//given, the cable lines that fragment carries are built as
//well and handed back through it -- they are not part of a
+19
View File
@@ -1645,6 +1645,25 @@ void DiagramView::editDiagramProperties()
DiagramPropertiesDialog::diagramPropertiesDialog(m_diagram, diagramEditor());
}
/**
@brief DiagramView::buildFolio
Build the items of the folio this view shows, and those of the folios
its cross-references need, if the project was opened without them
(QET_LAZY_FOLIOS); then fit the view to them.
*/
void DiagramView::buildFolio()
{
if (QETProject *project = m_diagram->project()) {
if (!project->unloadedFolioCount()) {
return;
}
project->buildFolioToShow(m_diagram);
} else {
m_diagram->ensureLoaded();
}
adjustSceneRect();
}
/**
@brief DiagramView::adjustSceneRect
Calcul and set the area of the scene visualized by this view
+1
View File
@@ -207,6 +207,7 @@ class DiagramView : public PaletteGraphicsView
void paste(const QPointF & = QPointF(), QClipboard::Mode = QClipboard::Clipboard);
void pasteHere();
void duplicate(const QPoint &stepOffset);
void buildFolio();
void adjustSceneRect();
void updateWindowTitle();
void resetConductors();
+58 -11
View File
@@ -30,11 +30,10 @@
@param prj the project where we must find element
@param diagram the diagram to exclude from the search
*/
ElementProvider::ElementProvider(QETProject *prj, Diagram *diagram)
{
m_diagram_list = prj->diagrams();
m_diagram_list.removeOne(diagram);
}
ElementProvider::ElementProvider(QETProject *prj, Diagram *diagram) :
m_project(prj),
m_excluded(diagram)
{}
/**
@brief ElementProvider::ElementProvider Constructor
@@ -44,6 +43,46 @@ ElementProvider::ElementProvider(Diagram *diag) {
m_diagram_list << diag;
}
/**
@brief ElementProvider::ElementProvider Constructor
@param diagrams the diagrams to search, e.g. QETProject::builtFolios()
*/
ElementProvider::ElementProvider(const QList<Diagram *> &diagrams) :
m_diagram_list(diagrams)
{}
/**
@brief ElementProvider::folios
@return the folios to search for symbols of the kinds @p kinds (free
ones only if @p free_only). For a project, the folios not built yet
(QET_LAZY_FOLIOS) holding such a symbol are built first, and only them
(QETProject::buildFoliosHolding()).
*/
QList<Diagram *> ElementProvider::folios(ElementData::Types kinds, bool free_only) const
{
if (!m_project) {
return m_diagram_list;
}
m_project->buildFoliosHolding(kinds, free_only);
QList<Diagram *> list = m_project->builtFolios();
list.removeOne(m_excluded);
return list;
}
/**
@brief ElementProvider::allFolios
@return the folios to search for anything else: every one, built
*/
QList<Diagram *> ElementProvider::allFolios() const
{
if (!m_project) {
return m_diagram_list;
}
QList<Diagram *> list = m_project->diagrams();
list.removeOne(m_excluded);
return list;
}
/**
@brief ElementProvider::FreeElement
Search and return the asked element corresponding with the given filter
@@ -57,16 +96,22 @@ QVector <QPointer<Element>> ElementProvider::freeElement(ElementData::Types filt
{
QVector<QPointer<Element>> free_elmt;
QList<Element *> elmt_list;
const QList<Diagram *> searched = folios(filter, true);
//A symbol whose saved partner is on a folio not built yet
//(QET_LAZY_FOLIOS) is not linked yet, but is not free: it will be
const QSet<QUuid> waiting = m_project ? m_project->symbolsOnUnbuiltFolios()
: QSet<QUuid>();
//search in all diagram
for (const auto &diagram_ : std::as_const(m_diagram_list))
for (const auto &diagram_ : std::as_const(searched))
{
//get all element in diagram d
elmt_list = diagram_->elements();
for (const auto &elmt_ : std::as_const(elmt_list))
{
if (filter & elmt_->elementData().m_type &&
elmt_->isFree())
elmt_->isFree() &&
!elmt_->pendingLinks().intersects(waiting))
{
free_elmt << elmt_;
}
@@ -86,7 +131,7 @@ QList <Element *> ElementProvider::fromUuids(QList<QUuid> uuid_list) const
{
QList <Element *> found_element;
foreach (Diagram *d, m_diagram_list) {
foreach (Diagram *d, allFolios()) {
foreach(Element *elmt, d->elements()) {
if (uuid_list.contains(elmt->uuid())) {
found_element << elmt;
@@ -106,7 +151,8 @@ QList <Element *> ElementProvider::fromUuids(QList<QUuid> uuid_list) const
QVector<QPointer<Element>> ElementProvider::find(ElementData::Types elmt_type) const
{
QVector<QPointer<Element>> returned_vector;
for (const auto &diagram_ : std::as_const(m_diagram_list))
const QList<Diagram *> searched = folios(elmt_type, false);
for (const auto &diagram_ : std::as_const(searched))
{
const auto elmt_list = diagram_->elements();
for (const auto &elmt_ : elmt_list)
@@ -153,7 +199,7 @@ QVector<QetGraphicsTableItem *> ElementProvider::table(
}
}
for (auto d : m_diagram_list) {
for (auto d : allFolios()) {
for (auto item_ : d->items())
{
if(item_->type() == QetGraphicsTableItem::Type)
@@ -197,8 +243,9 @@ QetGraphicsTableItem *ElementProvider::tableFromUuid(const QUuid &uuid)
QVector<TerminalElement *> ElementProvider::freeTerminal() const
{
QVector<TerminalElement *> vector_;
const QList<Diagram *> searched = folios(ElementData::Terminal, false);
for (const auto &diagram : std::as_const(m_diagram_list))
for (const auto &diagram : std::as_const(searched))
{
const auto elmt_list{diagram->elements()};
+9
View File
@@ -41,6 +41,7 @@ class ElementProvider
public:
ElementProvider(QETProject *prj, Diagram *diagram=nullptr);
ElementProvider(Diagram *diag);
explicit ElementProvider(const QList<Diagram *> &diagrams);
QVector <QPointer<Element>> freeElement(ElementData::Types filter) const;
QList <Element *> fromUuids(QList <QUuid>) const;
QVector<QPointer<Element> > find(ElementData::Types elmt_type) const;
@@ -49,7 +50,15 @@ class ElementProvider
QVector<TerminalElement *> freeTerminal() const;
private:
QList<Diagram *> folios(ElementData::Types kinds, bool free_only) const;
QList<Diagram *> allFolios() const;
QList <Diagram *> m_diagram_list;
//Searching a project: its folios are asked for when searched,
//so that a project opened with QET_LAZY_FOLIOS builds only
//those a search can find something on
QETProject *m_project = nullptr;
Diagram *m_excluded = nullptr;
};
#endif // ELEMENTPROVIDER_H
+4 -2
View File
@@ -473,14 +473,16 @@ void ElementsPanelWidget::updateButtons()
if (!selected_diagrams.isEmpty()) {
QETProject *selected_diagram_project = selected_diagrams.first() -> project();
bool is_writable = !(selected_diagram_project -> isReadOnly());
int project_diagrams_count = selected_diagram_project -> diagrams().count();
//Positions only: folios() does not build the folios' items
const QList<Diagram *> folios = selected_diagram_project -> folios();
int project_diagrams_count = folios.count();
// Find the highest (min) and lowest (max) index among the selection
int min_position = project_diagrams_count;
int max_position = -1;
for (Diagram *diagram : selected_diagrams) {
int pos = selected_diagram_project -> diagrams().indexOf(diagram);
int pos = folios.indexOf(diagram);
if (pos < min_position) min_position = pos;
if (pos > max_position) max_position = pos;
}
+4 -2
View File
@@ -253,11 +253,13 @@ QTreeWidgetItem *GenericPanel::fillProjectItem(QTreeWidgetItem *project_qtwi,
this,&GenericPanel::projectDiagramsOrderChanged);
} else {
// remove diagrams unknown to the project (presumably removed)
removeObsoleteItems(project -> diagrams(),
removeObsoleteItems(project -> folios(),
project_qtwi, QET::Diagram, false);
}
int index = 0;
foreach (Diagram *diagram, project -> diagrams()) {
//A folio's entry shows its own data (title, folio label):
//folios(), which does not build the folios' items
foreach (Diagram *diagram, project -> folios()) {
QTreeWidgetItem *diagram_qtwi = addDiagram(diagram,
nullptr,
options);
+1 -1
View File
@@ -208,7 +208,7 @@ void ProjectPrintWindow::requestPaint()
#endif
#endif
if (!m_project->diagrams().count()) {
if (!m_project->folios().count()) {
return;
}
+10 -2
View File
@@ -959,7 +959,9 @@ void ProjectView::loadDiagrams()
{
if (!m_project) return;
setDisplayFallbackWidget(m_project -> diagrams().isEmpty());
//The folios' items are built when their tab is first shown
//(tabChanged()), not here: a tab needs only the folio's own data
setDisplayFallbackWidget(m_project -> folios().isEmpty());
auto dialog = DialogWaiting::instance();
if(dialog)
@@ -968,7 +970,7 @@ void ProjectView::loadDiagrams()
"...</b><br/>Creating sheet tabs :</p>"));
}
for(auto diagram : m_project->diagrams())
for(auto diagram : m_project->folios())
{
if(dialog)
{
@@ -1202,6 +1204,12 @@ void ProjectView::rebuildDiagramsMap()
*/
void ProjectView::tabChanged(int tab_id)
{
//A folio opened without its items (QET_LAZY_FOLIOS) is built when
//its tab is first shown. Read the tab itself: the first tab is
//shown while loadDiagrams() is still filling m_diagram_ids
if (auto dv = qobject_cast<DiagramView *>(m_tab->widget(tab_id)))
dv->buildFolio();
if (tab_id == -1)
setDisplayFallbackWidget(true);
else if(m_tab->count() == 1)
+20 -2
View File
@@ -770,6 +770,15 @@ bool QET::compareCanonicalFilePaths(const QString &first, const QString &second)
@return false if an error occurred, true otherwise
*/
bool QET::writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString *error_message)
{
return writeXmlText(xml_doc.toString(4), filepath, error_message);
}
/**
@brief QET::writeXmlText
writeXmlFile() for a document already written as @p text
*/
bool QET::writeXmlText(const QString &text, const QString &filepath, QString *error_message)
{
QSaveFile file(filepath);
@@ -789,7 +798,7 @@ bool QET::writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString *
QTextStream out(&file);
out.setEncoding(QStringConverter::Utf8);
out.setGenerateByteOrderMark(false);
out << xml_doc.toString(4);
out << text;
if (!file.commit())
{
if (error_message) {
@@ -895,6 +904,15 @@ QActionGroup *QET::depthActionGroup(QObject *parent)
}
bool QET::writeToFile(QDomDocument &xml_doc, QFile *file, QString *error_message)
{
return writeTextToFile(xml_doc.toString(4), file, error_message);
}
/**
@brief QET::writeTextToFile
writeToFile() for a document already written as @p text
*/
bool QET::writeTextToFile(const QString &text, QFile *file, QString *error_message)
{
bool opened_here = file->isOpen() ? false : true;
@@ -920,7 +938,7 @@ bool QET::writeToFile(QDomDocument &xml_doc, QFile *file, QString *error_message
out.seek(0);
out.setEncoding(QStringConverter::Utf8);
out.setGenerateByteOrderMark(false);
out << xml_doc.toString(4);
out << text;
if (opened_here) {
file->close();
}
+2
View File
@@ -202,6 +202,8 @@ namespace QET {
bool compareCanonicalFilePaths(const QString &, const QString &);
bool writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString * error_message= nullptr);
bool writeToFile (QDomDocument &xml_doc, QFile *file, QString *error_message = nullptr);
bool writeXmlText(const QString &text, const QString &filepath, QString *error_message = nullptr);
bool writeTextToFile(const QString &text, QFile *file, QString *error_message = nullptr);
bool eachStrIsEqual (const QStringList &qsl);
QActionGroup *depthActionGroup(QObject *parent = nullptr);
void saveCustomColors();
+8 -4
View File
@@ -562,8 +562,9 @@ void QETDiagramEditor::setUpActions()
m_draw_grid->setCheckable(true);
m_draw_grid->setChecked(settings.value("diagrameditor/grid_display_startup", true).toBool());
connect(m_draw_grid, &QAction::triggered, [this](bool checked) {
//The folio's own setting: folios() builds nothing
foreach (ProjectView *prjv, this->openedProjects())
foreach (Diagram *d, prjv->project()->diagrams()) {
foreach (Diagram *d, prjv->project()->folios()) {
d->setDisplayGrid(checked);
d->update();
}
@@ -603,8 +604,9 @@ void QETDiagramEditor::setUpActions()
m_draw_guides->setCheckable(true);
m_draw_guides->setChecked(settings.value("diagrameditor/guides_display_startup", false).toBool());
connect(m_draw_guides, &QAction::triggered, [this](bool checked) {
//The folio's own setting: folios() builds nothing
foreach (ProjectView *prjv, this->openedProjects())
foreach (Diagram *d, prjv->project()->diagrams()) {
foreach (Diagram *d, prjv->project()->folios()) {
d->setDisplayGuides(checked);
}
});
@@ -652,8 +654,10 @@ void QETDiagramEditor::setUpActions()
action->setData(int(kind.first));
connect(action, &QAction::triggered, this, [kind](bool checked) {
ShownKinds::setShown(kind.first, checked);
//Items built later are shown or hidden as they are made
//(ShownKinds::tag())
for (QETProject *project : QETApp::registeredProjects())
for (Diagram *diagram : project->diagrams())
for (Diagram *diagram : project->builtFolios())
ShownKinds::apply(diagram, kind.first);
emit QETApp::instance()->shownKindsChanged();
});
@@ -2004,7 +2008,7 @@ bool QETDiagramEditor::openAndAddProject(
//was not found: they would otherwise vanish on the next save
//without the user ever being told.
QStringList lost_wires;
for (Diagram *diagram : project->diagrams()) {
for (Diagram *diagram : project->foliosWithWiresNotReconnected()) {
for (const QString &wire : diagram->wiresNotReconnected()) {
lost_wires << tr("Sheet %1: %2").arg(diagram->folioIndex() + 1).arg(wire);
}
@@ -37,7 +37,8 @@
*/
ProjectDBModel::ProjectDBModel(QETProject *project, QObject *parent) :
QAbstractTableModel(parent),
m_project(project)
m_project(project),
m_fill_when_built(project->dataBase()->isBuildingFolio())
{
connect(m_project->dataBase(), &projectDataBase::dataBaseUpdated, this, &ProjectDBModel::dataBaseUpdated);
}
@@ -320,6 +321,21 @@ void ProjectDBModel::setIdentifier(const QString &identifier) {
m_identifier = identifier;
}
/**
@brief ProjectDBModel::folioBuilt
The folio of a table made while it was being built (QET_LAZY_FOLIOS)
is built: read the database, as opening does once it has filled it.
Nothing for any other model.
*/
void ProjectDBModel::folioBuilt()
{
if (!m_fill_when_built) {
return;
}
m_fill_when_built = false;
dataBaseUpdated();
}
/**
@brief ProjectDBModel::dataBaseUpdated
slot called when the project database is updated
@@ -374,6 +390,13 @@ void ProjectDBModel::setHeaderString()
void ProjectDBModel::fillValue()
{
m_record.clear();
//Opening builds a table before the database is filled, and fills
//it after: a table on a folio built late (QET_LAZY_FOLIOS) does
//the same, so that it is laid out as it would have been on
//opening -- empty until its folio is built (folioBuilt())
if (m_fill_when_built) {
return;
}
auto query_ = m_project->dataBase()->newQuery(m_query);
if (!query_.exec()) {
@@ -71,6 +71,7 @@ class ProjectDBModel : public QAbstractTableModel
void setIdentifier(const QString &identifier);
QString identifier() const {return m_identifier;}
static QString xmlTagName() {return QString("project_data_base_model");}
void folioBuilt();
private:
void dataBaseUpdated();
@@ -79,6 +80,7 @@ class ProjectDBModel : public QAbstractTableModel
private:
QPointer<QETProject> m_project;
bool m_fill_when_built = false;
QString m_query;
QVector<QStringList> m_record;
//First int = section, second int = Qt::role, QVariant = value
@@ -565,15 +565,34 @@ void QetGraphicsTableItem::initLink()
{
if (!m_pending_previous_table_uuid.isNull())
{
ElementProvider provider_(this->diagram()->project());
if (auto previous_table = provider_.tableFromUuid(m_pending_previous_table_uuid)) {
//The built folios only: a previous table on a folio not built
//yet (QET_LAZY_FOLIOS) is linked when that folio is
QETProject *project = this->diagram()->project();
ElementProvider provider_(project->builtFolios());
auto previous_table = provider_.tableFromUuid(m_pending_previous_table_uuid);
if (previous_table) {
setPreviousTable(previous_table);
}
m_pending_previous_table_uuid = QUuid(); //Set to null in case initLink is called again
if (previous_table || !project->unloadedFolioCount()) {
m_pending_previous_table_uuid = QUuid(); //Set to null in case initLink is called again
}
}
setSize(m_pending_size);
}
/**
@brief QetGraphicsTableItem::previousTableUuid
@return the uuid of the table before this one in its chain, linked or
still waiting for its folio to be built (QET_LAZY_FOLIOS); null if none
*/
QUuid QetGraphicsTableItem::previousTableUuid() const
{
if (m_previous_table) {
return m_previous_table->uuid();
}
return m_pending_previous_table_uuid;
}
/**
@brief QetGraphicsTableItem::minimumRowHeight
@return the minimum height of a row
@@ -610,7 +629,7 @@ QDomElement QetGraphicsTableItem::toXml(QDomDocument &dom_document) const
const auto project_ = diagram() ? diagram()->project() : nullptr;
auto in_project = [project_](const QetGraphicsTableItem *table) {
return project_ && table->diagram()
&& project_->diagrams().contains(table->diagram());
&& project_->folios().contains(table->diagram());
};
QetGraphicsTableItem *previous_ = m_previous_table;
while (previous_ && !in_project(previous_)) {
@@ -84,6 +84,8 @@ class QetGraphicsTableItem : public QetGraphicsItem
QPointer<QetGraphicsTableItem> nextTable() const;
void setToMinimumHeight();
void initLink();
bool hasPendingLink() const {return !m_pending_previous_table_uuid.isNull();}
QUuid previousTableUuid() const;
QUuid uuid() const {return m_uuid;}
int minimumRowHeight() const;
+75 -1
View File
@@ -25,6 +25,7 @@
#include "../conductorsegment.h"
#include "../conductorsegmentprofile.h"
#include "../diagram.h"
#include "../dataBase/projectdatabase.h"
#include "../diagramcommands.h"
#include "../qetdiagrameditor.h"
#include "../qetgraphicsitem/terminal.h"
@@ -118,6 +119,8 @@ Conductor::Conductor(Terminal *p1, Terminal* p2) :
setZValue(11);
m_previous_z_value = zValue();
m_uuid = QUuid::createUuid();
static quint64 created = 0;
m_creation_rank = ++created;
//Add this conductor to the list of conductors of each of the two terminals
bool ajout_p1 = terminal1 -> addConductor(this);
@@ -1114,6 +1117,7 @@ void Conductor::pointsToSegments(const QList<QPointF>& points_list) {
*/
bool Conductor::fromXml(QDomElement &dom_element)
{
const QUuid old_uuid = m_uuid;
//Older project files have no conductor uuid attribute at all --
//generate one on load, same treatment terminal uuids got when
//that field was introduced (see terminal1/terminal2 handling in
@@ -1129,6 +1133,7 @@ bool Conductor::fromXml(QDomElement &dom_element)
//single load-and-resave (see #754).
m_uuid = QUuid::createUuid();
}
uuidChanged(old_uuid);
//"nan" and "inf" parse as numbers; a non-finite position is kept at 0
const qreal x = dom_element.attribute("x", nullptr).toDouble();
@@ -1235,7 +1240,7 @@ QDomElement Conductor::toXml(QDomDocument &dom_document,
dom_element.appendChild(dom_seq);
// Export the properties and text
m_properties. toXml(dom_element);
properties().toXml(dom_element);
if(m_text_item->wasMovedByUser())
{
dom_element.setAttribute("userx", QString::number(m_text_item->pos().x()));
@@ -1735,6 +1740,7 @@ void Conductor::refreshText()
m_properties.text = m_properties.m_formula;
m_text_item->setPlainText(m_properties.text);
}
storeProperties();
}
else
{
@@ -1833,6 +1839,7 @@ void Conductor::setProperties(const ConductorProperties &property)
calculateTextItemPosition();
update();
storeProperties();
emit propertiesChange();
}
@@ -1842,9 +1849,75 @@ void Conductor::setProperties(const ConductorProperties &property)
*/
ConductorProperties Conductor::properties() const
{
if (projectDataBase *store = placedStore())
return store->conductorProperties(m_uuid);
return(m_properties);
}
/**
@brief Conductor::placedStore
@return the project database holding this wire's properties
(DB-ACCESSORS-PLAN.md stage 4.5), or nullptr if it is not placed in a
project, or shares its uuid with another wire for the moment a copy
takes to get its own
*/
projectDataBase *Conductor::placedStore() const
{
Diagram *d = diagram();
QETProject *project = d ? d->project() : nullptr;
projectDataBase *store = project ? project->dataBase() : nullptr;
return store && store->hasConductorProperties(m_uuid)
&& store->placedConductorCount(m_uuid) == 1 ? store : nullptr;
}
/**
@brief Conductor::storeProperties
Write this wire's properties to its project's store, once it is placed:
every change of a placed wire's properties comes through here.
*/
void Conductor::storeProperties()
{
Diagram *d = diagram();
QETProject *project = d ? d->project() : nullptr;
if (project && project->dataBase() && project->dataBase()->hasConductorProperties(m_uuid))
project->dataBase()->conductorPropertiesStored(this, m_properties);
}
/**
@brief Conductor::newUuid
Make a new uuid for this conductor; its stored properties and its row
follow it.
*/
void Conductor::newUuid()
{
setUuid(QUuid::createUuid());
}
/**
@brief Conductor::setUuid
@param uuid : the uuid of this conductor, saved from now on
*/
void Conductor::setUuid(const QUuid &uuid)
{
const QUuid old_uuid = m_uuid;
m_uuid = uuid;
m_persist_uuid = true;
uuidChanged(old_uuid);
}
/**
@brief Conductor::uuidChanged
Tell the project's store this placed wire's uuid changed from
@p old_uuid (a paste renews it, a file gives it).
*/
void Conductor::uuidChanged(const QUuid &old_uuid)
{
Diagram *d = diagram();
QETProject *project = d ? d->project() : nullptr;
if (project && project->dataBase())
project->dataBase()->conductorUuidChanged(this, old_uuid);
}
/**
@brief Conductor::setCableReference
Record the cable core this conductor shows, together with the label
@@ -1868,6 +1941,7 @@ void Conductor::setCableReference(const QString &label,
m_properties.m_cable = label;
m_properties.m_cable_uuid = cable_uuid;
m_properties.m_cable_slot = slot;
storeProperties();
emit propertiesChange();
}
+12 -2
View File
@@ -27,6 +27,7 @@
class ConductorProfile;
class ConductorSegmentProfile;
class Diagram;
class projectDataBase;
class Terminal;
class ConductorSegment;
class ConductorTextItem;
@@ -80,8 +81,8 @@ class Conductor : public QGraphicsObject
Diagram *diagram() const;
ConductorTextItem *textItem() const;
QUuid uuid() const {return m_uuid;}
void newUuid() {m_uuid = QUuid::createUuid(); m_persist_uuid = true;} //create new uuid for this conductor
void setUuid(const QUuid &uuid) {m_uuid = uuid; m_persist_uuid = true;} //saved from now on
void newUuid(); //create new uuid for this conductor
void setUuid(const QUuid &uuid); //saved from now on
void updatePath(const QRectF & = QRectF());
//This method do nothing, it's only made to be used with Q_PROPERTY
@@ -125,6 +126,11 @@ class Conductor : public QGraphicsObject
bool only_text = false);
void setProperties(const ConductorProperties &property);
ConductorProperties properties() const;
/// This wire's own copy of its properties, whatever the store holds
ConductorProperties ownProperties() const {return m_properties;}
/// The order wires were made in this session: a project's are
/// made in the order its file lists them
quint64 creationRank() const {return m_creation_rank;}
/// Records which cable core this conductor shows and writes the
/// label that assignment generates (15W1:br) into its cable
/// field. Deliberately not routed through
@@ -233,6 +239,10 @@ class Conductor : public QGraphicsObject
/// file had none -- toXml() must not persist that value, or
/// every reload mints and saves a new random one (see #754).
bool m_persist_uuid = true;
quint64 m_creation_rank = 0;
void uuidChanged(const QUuid &old_uuid);
projectDataBase *placedStore() const;
void storeProperties();
/// QPen et QBrush objects used to draw conductors
static QPen conductor_pen;
+117 -5
View File
@@ -18,6 +18,7 @@
#include "element.h"
#include "../qetapp.h"
#include "../qetproject.h"
#include "../dataBase/projectdatabase.h"
#include "../PropertiesEditor/propertieseditordialog.h"
#include "../autoNum/assignvariables.h"
#include "../autoNum/elementautonumschemecommand.h"
@@ -718,6 +719,7 @@ bool Element::buildFromXml(const QDomElement &xml_def_elmt, int *state)
m_data.m_informations.fromXml(
xml_def_elmt.firstChildElement(QStringLiteral("elementInformations")),
QStringLiteral("elementInformation"));
storeInformation();
//scroll of the Children of the Definition: Parts of the Drawing
int parsed_elements_count = 0;
@@ -1042,7 +1044,9 @@ bool Element::fromXml(QDomElement &e,
}
//uuid of this element
m_uuid = QUuid(e.attribute(QStringLiteral("uuid"), QUuid::createUuid().toString()));
//Through setUuid(): a folio adds a symbol before reading it, so its
//stored information has to follow from the uuid it was born with.
setUuid(QUuid(e.attribute(QStringLiteral("uuid"), QUuid::createUuid().toString())));
//load prefix
m_prefix = e.attribute(QStringLiteral("prefix"));
@@ -1265,10 +1269,11 @@ QDomElement Element::toXml(
}
//save information of this element
if (! m_data.m_informations.keys().isEmpty()) {
const DiagramContext informations = elementInformations();
if (! informations.keys().isEmpty()) {
QDomElement infos =
document.createElement(QStringLiteral("elementInformations"));
m_data.m_informations.toXml(infos, QStringLiteral("elementInformation"));
informations.toXml(infos, QStringLiteral("elementInformation"));
//toXml() skips empty values: an element whose information is
//all empty would otherwise be written an empty block, which the
//next load reads as no information and the next save drops.
@@ -1625,7 +1630,10 @@ void Element::initLink(QETProject *prj)
// if nothing to link return now
if (tmp_uuids_link.isEmpty()) return;
ElementProvider ep(prj);
//The built folios only: a partner on a folio not built yet
//(QET_LAZY_FOLIOS) is linked when its folio is, and the saved list
//is kept until then, for the order it gives
ElementProvider ep(prj->builtFolios());
QList<QUuid> uuids;
for (const auto &linkInfo : tmp_uuids_link) {
uuids.append(linkInfo.uuid);
@@ -1643,7 +1651,27 @@ void Element::initLink(QETProject *prj)
}
}
restoreSavedLinkOrder();
tmp_uuids_link.clear();
if (pendingLinks().isEmpty() || !prj->unloadedFolioCount()) {
tmp_uuids_link.clear();
}
}
/**
@brief Element::pendingLinks
@return the uuids of the partners this symbol was saved linked to and
is not linked to yet: those on a folio not built yet (QET_LAZY_FOLIOS),
until initLink() runs again once it is
*/
QSet<QUuid> Element::pendingLinks() const
{
QSet<QUuid> pending;
for (const auto &link_info : tmp_uuids_link) {
pending.insert(link_info.uuid);
}
for (Element *elmt : connected_elements) {
pending.remove(elmt->uuid());
}
return pending;
}
/**
@@ -1674,6 +1702,23 @@ void Element::initLink(const QList<Element *> &candidates)
tmp_uuids_link.clear();
}
/**
@brief Element::waitingLinks
@return pendingLinks() in the order this symbol was saved with them,
each with its group index (-1 for none)
*/
QList<std::pair<QUuid, int>> Element::waitingLinks() const
{
const QSet<QUuid> pending = pendingLinks();
QList<std::pair<QUuid, int>> list;
for (const auto &link_info : tmp_uuids_link) {
if (pending.contains(link_info.uuid)) {
list.append({link_info.uuid, link_info.group_index});
}
}
return list;
}
/**
@brief Element::restoreSavedLinkOrder
Put connected_elements back in the order tmp_uuids_link saved them.
@@ -1795,6 +1840,33 @@ void Element::setFormulaSchemeId(const QUuid &id)
m_data.m_informations.addValue(QETInformation::ELMT_FORMULA_ID,
id.toString(), false);
}
storeInformation();
}
/**
@brief Element::setUuid
A placed symbol's stored information and its database rows follow its
uuid (a paste or a folio copy renews it).
*/
void Element::setUuid(const QUuid &uuid)
{
const QUuid old_uuid = m_uuid;
m_uuid = uuid;
if (diagram() && diagram()->project() && diagram()->project()->dataBase())
diagram()->project()->dataBase()->elementUuidChanged(this, old_uuid);
}
/**
@brief Element::storeInformation
Write this symbol's information to its project's store, once it is
placed (DB-ACCESSORS-PLAN.md stage 4.1): every change of a placed
symbol's information comes through here.
*/
void Element::storeInformation()
{
if (diagram() && diagram()->project() && diagram()->project()->dataBase()
&& diagram()->project()->dataBase()->hasElementInformation(m_uuid))
diagram()->project()->dataBase()->elementInformationChanged(this, m_data.m_informations);
}
void Element::setElementInformations(DiagramContext dc)
@@ -1811,6 +1883,7 @@ void Element::setElementInformations(DiagramContext dc)
if (!actual_label.isEmpty()) {
m_data.m_informations.addValue(QStringLiteral("label"), actual_label); //Update the label if there is a formula
}
storeInformation();
emit elementInfoChange(old_info, m_data.m_informations);
// Propagate label change to linked PLC slaves (label is changed via
@@ -1841,9 +1914,43 @@ void Element::setElementInformations(DiagramContext dc)
*/
ElementData Element::elementData() const
{
if (projectDataBase *store = placedStore()) {
ElementData data = m_data;
data.m_informations = store->elementInformation(m_uuid);
return data;
}
return m_data;
}
/**
@brief Element::elementInformations
@return this symbol's information: for a placed symbol the project's
store holds it (DB-ACCESSORS-PLAN.md stage 4.2), for one not placed
(a preview, the symbol editor) its own copy.
*/
DiagramContext Element::elementInformations() const
{
if (projectDataBase *store = placedStore())
return store->elementInformation(m_uuid);
return m_data.m_informations;
}
/**
@brief Element::placedStore
@return the project database holding this symbol's information, or
nullptr if it is not placed in a project
*/
projectDataBase *Element::placedStore() const
{
Diagram *d = diagram();
QETProject *project = d ? d->project() : nullptr;
projectDataBase *store = project ? project->dataBase() : nullptr;
//A uuid other placed symbols share (F100) names no single symbol's
//information: this one answers from its own copy.
return store && store->hasElementInformation(m_uuid)
&& store->placedElementCount(m_uuid) == 1 ? store : nullptr;
}
/**
* @brief Element::setElementData
* Set new data for this element.
@@ -1860,6 +1967,7 @@ void Element::setElementData(ElementData data)
if (old_info != m_data.m_informations) {
m_data.m_informations.addValue(QStringLiteral("label"), actualLabel()); //Update the label if there is a formula
storeInformation();
if (diagram()) {
diagram()->project()->dataBase()->elementInfoChanged(this);
}
@@ -2078,6 +2186,7 @@ void Element::setUpFormula(bool code_letter, QUndoCommand *parent_undo)
->elementAutoNumCurrentFormula();
m_data.m_informations.addValue(QStringLiteral("formula"), formula);
storeInformation();
QString element_currentAutoNum = diagram()
->project()
@@ -2146,6 +2255,7 @@ void Element::setUpFormula(bool code_letter, QUndoCommand *parent_undo)
{
DiagramContext dc = m_data.m_informations;
m_data.m_informations.addValue(QStringLiteral("label"), actualLabel());
storeInformation();
emit elementInfoChange(dc, m_data.m_informations);
}
}
@@ -2199,6 +2309,8 @@ void Element::freezeNewAddedElement()
*/
QString Element::actualLabel()
{
//The symbol's own copy, not the store: this runs while the
//information is being changed, before the store has the change.
if (m_data.m_informations.value(QStringLiteral("formula")).toString().isEmpty()) {
return m_data.m_informations.value(QStringLiteral("label")).toString();
} else {
+12 -4
View File
@@ -40,6 +40,8 @@ class QUndoCommand;
/**
This is the base class for electrical elements.
*/
class projectDataBase;
class Element : public QetGraphicsItem
{
friend class DiagramEventAddElement;
@@ -130,8 +132,9 @@ class Element : public QetGraphicsItem
}
//METHODS related to information
DiagramContext elementInformations()const
{return m_data.m_informations;}
DiagramContext elementInformations()const;
/// This symbol's own copy of its information, whatever the store holds
DiagramContext ownInformations() const {return m_data.m_informations;}
virtual void setElementInformations(DiagramContext dc);
ElementData elementData() const;
@@ -222,6 +225,9 @@ class Element : public QetGraphicsItem
virtual void unlinkAllElements() {}
virtual void unlinkElement(Element *) {}
virtual void initLink(QETProject *);
QSet<QUuid> pendingLinks() const;
bool keepsSavedLinks() const {return !tmp_uuids_link.isEmpty();}
QList<std::pair<QUuid, int>> waitingLinks() const;
/**
Resolve tmp_uuids_link against a caller-supplied candidate
list instead of a project-wide search (bugtracker #607).
@@ -259,8 +265,8 @@ class Element : public QetGraphicsItem
*/
QString linkTypeToString() const;
void newUuid() {m_uuid = QUuid::createUuid();} //create new uuid for this element
void setUuid(const QUuid &uuid) {m_uuid = uuid;}
void newUuid() {setUuid(QUuid::createUuid());} //create new uuid for this element
void setUuid(const QUuid &uuid);
protected:
void drawAxes(QPainter *, const QStyleOptionGraphicsItem *);
@@ -329,6 +335,8 @@ class Element : public QetGraphicsItem
private:
void restoreSavedLinkOrder();
void storeInformation();
projectDataBase *placedStore() const;
bool m_must_highlight = false;
QSize dimensions;
QPoint hotspot_coord;
+11
View File
@@ -1090,6 +1090,17 @@ QList<Terminal *> relatedPotentialTerminal (
// If terminal parent element is a folio report.
if (all_diagram && terminal -> parentElement() -> linkType() & Element::AllReport)
{
//The report's partner on a folio not built yet (QET_LAZY_FOLIOS)
//is built, so that a potential is followed as far as when every
//folio is; not while folios are being read or built, which
//would build whole report chains
Element *report = terminal->parentElement();
const QSet<QUuid> waiting = report->pendingLinks();
QETProject *project = report->diagram() ? report->diagram()->project() : nullptr;
if (!waiting.isEmpty() && project
&& project->state() != QETProject::ProjectParsingRunning) {
project->buildFoliosHoldingSymbols(waiting);
}
QList <Element *> elmt_list = terminal -> parentElement() -> linkedElements();
if (!elmt_list.isEmpty())
{
+791 -37
View File
File diff suppressed because it is too large Load Diff
+51 -1
View File
@@ -31,6 +31,7 @@
#include "wirehops.h"
#include "wiringrules.h"
#include "diagram.h"
#include "autoNum/assignvariables.h"
#ifdef BUILD_WITHOUT_KF
# include "ui/nokde/kautosavefile.h"
#else
@@ -44,6 +45,7 @@
#include <QFuture>
#include <array>
#include <optional>
class Diagram;
class Element;
@@ -119,6 +121,22 @@ class QETProject : public QObject
QUuid derivedItemUuid(const QString &kind, const QString &key);
ProjectState state() const;
QList<Diagram *> diagrams() const;
//The folios whether their items are built or not
//(QET_LAZY_FOLIOS, LAZY-FOLIO-PLAN.md); diagrams() builds them
QList<Diagram *> folios() const;
bool deferringFolios() const {return m_deferring_folios;}
int unloadedFolioCount() const;
QList<Diagram *> builtFolios() const;
QList<Diagram *> cableLabelFolios();
QList<Diagram *> foliosWithWiresNotReconnected();
void buildFolioToShow(Diagram *folio);
void buildFoliosHolding(ElementData::Types kinds, bool free_only);
void buildFoliosHoldingSymbols(const QSet<QUuid> &symbols);
QSet<QUuid> symbolsOnUnbuiltFolios() const;
QSet<Diagram *> tableChainFolios(Diagram *folio) const;
void loadFolios() const;
void buildFolios(const QList<Diagram *> &folios) const;
void folioLoaded(Diagram *folio);
int folioIndex(const Diagram *) const;
XmlElementCollection *embeddedElementCollection()const;
TitleBlockTemplatesProjectCollection *embeddedTitleBlockTemplatesCollection();
@@ -240,6 +258,18 @@ class QETProject : public QObject
const QString &ignored_title = QString()) const;
static QString normalizedAutoNumName(const QString &name);
QVector<Element *> elementsUsingElementAutoNum(const QString &title) const;
/// A placed symbol's numbering, whether its folio is built or
/// not (QET_LAZY_FOLIOS): what is asked of every symbol of the
/// project when one is numbered
struct SymbolNumbering {
const Element *element = nullptr; ///< null on a folio not built
QString label;
QString formula;
QUuid scheme; ///< the element numbering scheme it follows
autonum::sequentialNumbers sequence;
bool numbered = false; ///< neither a slave nor a report
};
QList<SymbolNumbering> symbolNumbering() const;
/**
* @brief Renumber existing elements by element autonumbering scheme.
@@ -283,7 +313,8 @@ class QETProject : public QObject
void autoFolioNumberingNewFolios ();
void autoFolioNumberingSelectedFolios(int, int, const QString&);
QDomDocument toXml();
QDomDocument toXml(QHash<int, QByteArray> *kept_folios = nullptr);
static QString spliceKeptFolios(QString text, const QHash<int, QByteArray> &kept_folios);
bool close();
QETResult write();
bool isReadOnly() const;
@@ -293,6 +324,7 @@ class QETProject : public QObject
QString integrateTitleBlockTemplate(const TitleBlockTemplateLocation &, MoveTitleBlockTemplatesHandler *handler);
bool usesElement(const ElementsLocation &) const;
QList <ElementsLocation> unusedElements() const;
QList<ElementsLocation> unbuiltFolioElements() const;
bool usesTitleBlockTemplate(const TitleBlockTemplateLocation &);
bool projectWasModified();
bool projectOptionsWereModified();
@@ -474,6 +506,24 @@ class QETProject : public QObject
/// saved without an id (written before ids existed)
bool m_legacy_element_autonums = false;
void linkElementsToElementAutoNums();
void linkElementsToElementAutoNums(Diagram *folio);
QHash<QString, QStringList> elementAutoNumTitlesByFormula() const;
std::optional<QUuid> elementAutoNumIdToLink(
const DiagramContext &information,
const QHash<QString, QStringList> &titles_by_formula) const;
bool m_deferring_folios = false;
/// Saving may write a folio not built as it was read
/// (writesKeptFolios()): the file is this version's, its
/// database rows came from it, and nothing project-wide that
/// a folio's saved items depend on has changed since
bool m_kept_folios_trusted = false;
bool m_kept_folios_stale = false;
/// Something was edited since the project was opened: a table
/// filled from the database may have changed
bool m_edited_since_open = false;
void markKeptFoliosStale() {m_kept_folios_stale = true;}
/// The file being read, for each folio's text (readDiagramsXml())
QByteArray m_opened_content;
bool m_auto_conductor = true;
WireHops::Mode m_wire_hops = WireHops::Mode::None;
/// Texts drawn in a turned symbol stay horizontal (on for a new
+4 -2
View File
@@ -150,8 +150,9 @@ void DiagramBgColorToolButton::applyColor(const QColor &color)
if (!editor) {
return;
}
//A folio not built yet takes the colour when it is drawn
for (ProjectView *pv : editor->openedProjects())
for (Diagram *d : pv->project()->diagrams())
for (Diagram *d : pv->project()->folios())
d->update();
}
@@ -175,8 +176,9 @@ void DiagramBgColorToolButton::applySystemColor()
if (!editor) {
return;
}
//A folio not built yet takes the colour when it is drawn
for (ProjectView *pv : editor->openedProjects())
for (Diagram *d : pv->project()->diagrams())
for (Diagram *d : pv->project()->folios())
d->update();
}
@@ -60,7 +60,8 @@ void DiagramEditorHandlerSizeWidget::on_comboBox_currentIndexChanged(int index)
{
editor_->setProperty("graphics_handler_size", size);
for (auto project_view : editor_->openedProjects()) {
for (auto diagram : project_view->project()->diagrams()) {
//Handles exist on built folios only
for (auto diagram : project_view->project()->builtFolios()) {
for (const auto item : diagram->items()) {
if (item->type() == QetGraphicsHandlerItem::Type) {
auto handler = qgraphicsitem_cast<QetGraphicsHandlerItem *>(item);
+3 -2
View File
@@ -169,7 +169,7 @@ void JumpToElementDialog::activateCurrentItem()
const int index = current->data(Qt::UserRole).toInt();
if (index == -1) {
const QList<Diagram *> diagrams = m_diagram->project()
? m_diagram->project()->diagrams()
? m_diagram->project()->folios()
: QList<Diagram *>();
const int folio = current->data(Qt::UserRole + 2).toInt();
if (folio >= 0 && folio < diagrams.size()) {
@@ -208,7 +208,8 @@ QListWidgetItem *JumpToElementDialog::cellItem(const QString &needle) const
if (!m_diagram || !m_diagram->project()) {
return nullptr;
}
const QList<Diagram *> diagrams = m_diagram->project()->diagrams();
//Folios' cells only (their border): folios() builds nothing
const QList<Diagram *> diagrams = m_diagram->project()->folios();
Diagram *diagram = m_diagram;
QString cell = needle;