mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-15 10:04:13 +02:00
Merge pull request #1468 from qelectrotech/resolve-1461
Speed up opening big projects with lazy folios
This commit is contained in:
@@ -1011,6 +1011,15 @@ bool TerminalStrip::fromXml(QDomElement &xml_element)
|
|||||||
auto xml_layout = xml_element.firstChildElement(QStringLiteral("layout"));
|
auto xml_layout = xml_element.firstChildElement(QStringLiteral("layout"));
|
||||||
if (!xml_layout.isNull())
|
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
|
//Get all free elements terminal of the project
|
||||||
const ElementProvider ep(m_project);
|
const ElementProvider ep(m_project);
|
||||||
auto free_terminals = ep.freeTerminal();
|
auto free_terminals = ep.freeTerminal();
|
||||||
|
|||||||
@@ -105,7 +105,9 @@ QList<Diagram *> AutoNumSchemeCommand::usersOf(const QETProject *project, Kind k
|
|||||||
if (!project || title.isEmpty()) {
|
if (!project || title.isEmpty()) {
|
||||||
return users;
|
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 (kind == Kind::Conductor) {
|
||||||
if (diagram->conductorsAutonumName() == title) {
|
if (diagram->conductorsAutonumName() == title) {
|
||||||
users << diagram;
|
users << diagram;
|
||||||
@@ -234,7 +236,8 @@ void AutoNumSchemeCommand::rename(const QString &from, const QString &to)
|
|||||||
if (m_project->conductorCurrentAutoNum() == from) {
|
if (m_project->conductorCurrentAutoNum() == from) {
|
||||||
m_project->setCurrentConductorAutoNum(to);
|
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
|
//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}) {
|
for (auto *hash : {&d->m_cnd_unitfolio_max, &d->m_cnd_tenfolio_max, &d->m_cnd_hundredfolio_max}) {
|
||||||
if (hash->contains(from)) {
|
if (hash->contains(from)) {
|
||||||
|
|||||||
@@ -369,10 +369,19 @@ ElementAutoNumSchemeCommand::NumberSupport ElementAutoNumSchemeCommand::numberSu
|
|||||||
std::optional<int> ElementAutoNumSchemeCommand::numberOf(const NumberSupport &support,
|
std::optional<int> ElementAutoNumSchemeCommand::numberOf(const NumberSupport &support,
|
||||||
const Element *element)
|
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;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
const autonum::sequentialNumbers seq = element->sequenceStruct();
|
|
||||||
const QStringList &list = support.type == QLatin1String("unit") ? seq.unit
|
const QStringList &list = support.type == QLatin1String("unit") ? seq.unit
|
||||||
: support.type == QLatin1String("ten") ? seq.ten
|
: support.type == QLatin1String("ten") ? seq.ten
|
||||||
: seq.hundred;
|
: seq.hundred;
|
||||||
@@ -385,14 +394,18 @@ std::optional<int> ElementAutoNumSchemeCommand::numberOf(const NumberSupport &su
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
/// The elements of @p project which follow the scheme @p title and are not slaves or reports
|
/// The symbols of @p project which follow the scheme @p title and are not
|
||||||
QVector<Element *> numberedFollowers(const QETProject *project, const QString &title)
|
/// slaves or reports, built or not (QETProject::symbolNumbering())
|
||||||
|
QList<QETProject::SymbolNumbering> numberedFollowers(const QETProject *project, const QString &title)
|
||||||
{
|
{
|
||||||
QVector<Element *> list;
|
QList<QETProject::SymbolNumbering> list;
|
||||||
const auto followers = project->elementsUsingElementAutoNum(title);
|
const QUuid id = project->elementAutoNumId(title);
|
||||||
for (Element *el : followers) {
|
if (id.isNull()) {
|
||||||
if (el->linkType() != Element::Slave && !(el->linkType() & Element::AllReport)) {
|
return list;
|
||||||
list << el;
|
}
|
||||||
|
for (const auto &symbol : project->symbolNumbering()) {
|
||||||
|
if (symbol.numbered && !symbol.formula.isEmpty() && symbol.scheme == id) {
|
||||||
|
list << symbol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
@@ -411,13 +424,10 @@ QSet<QString> ElementAutoNumSchemeCommand::labelsHeldBesides(const QETProject *p
|
|||||||
if (!project) {
|
if (!project) {
|
||||||
return labels;
|
return labels;
|
||||||
}
|
}
|
||||||
for (Diagram *d : project->diagrams()) {
|
//Folios not built yet answer from what building them would give
|
||||||
for (QGraphicsItem *item : d->items()) {
|
for (const auto &symbol : project->symbolNumbering()) {
|
||||||
if (auto *el = qgraphicsitem_cast<Element *>(item)) {
|
if (symbol.element != element) {
|
||||||
if (el != element) {
|
labels << symbol.label;
|
||||||
labels << el->elementInformations().value(QETInformation::ELMT_LABEL).toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
labels.remove(QString());
|
labels.remove(QString());
|
||||||
@@ -453,8 +463,8 @@ std::optional<ElementAutoNumSchemeCommand::CounterConflict> ElementAutoNumScheme
|
|||||||
|
|
||||||
CounterConflict conflict;
|
CounterConflict conflict;
|
||||||
conflict.counter = counter;
|
conflict.counter = counter;
|
||||||
for (const Element *el : numberedFollowers(project, title)) {
|
for (const auto &symbol : numberedFollowers(project, title)) {
|
||||||
if (const auto number = numberOf(support, el)) {
|
if (const auto number = numberOf(support, symbol.sequence)) {
|
||||||
if (*number >= counter) {
|
if (*number >= counter) {
|
||||||
++conflict.count;
|
++conflict.count;
|
||||||
conflict.highest = std::max(conflict.highest, *number);
|
conflict.highest = std::max(conflict.highest, *number);
|
||||||
@@ -486,8 +496,8 @@ QList<ElementAutoNumSchemeCommand::GapRange> ElementAutoNumSchemeCommand::gapRan
|
|||||||
return ranges;
|
return ranges;
|
||||||
}
|
}
|
||||||
QSet<int> used;
|
QSet<int> used;
|
||||||
for (const Element *el : numberedFollowers(project, title)) {
|
for (const auto &symbol : numberedFollowers(project, title)) {
|
||||||
if (const auto number = numberOf(support, el)) {
|
if (const auto number = numberOf(support, symbol.sequence)) {
|
||||||
used << *number;
|
used << *number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -559,22 +569,19 @@ QList<int> ElementAutoNumSchemeCommand::freeNumbers(const QETProject *project,
|
|||||||
|
|
||||||
QSet<int> used;
|
QSet<int> used;
|
||||||
int highest = context.itemAt(support.partIndex).value(1).toInt();
|
int highest = context.itemAt(support.partIndex).value(1).toInt();
|
||||||
for (const Element *el : numberedFollowers(project, title)) {
|
for (const auto &symbol : numberedFollowers(project, title)) {
|
||||||
if (el == element) continue;
|
if (symbol.element == element) continue;
|
||||||
if (const auto number = numberOf(support, el)) {
|
if (const auto number = numberOf(support, symbol.sequence)) {
|
||||||
used << *number;
|
used << *number;
|
||||||
highest = std::max(highest, *number);
|
highest = std::max(highest, *number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QSet<QString> labels;
|
QSet<QString> labels;
|
||||||
for (Diagram *d : project->diagrams()) {
|
//Folios not built yet answer from what building them would give
|
||||||
for (QGraphicsItem *item : d->items()) {
|
for (const auto &symbol : project->symbolNumbering()) {
|
||||||
if (auto *el = qgraphicsitem_cast<Element *>(item)) {
|
if (symbol.element != element) {
|
||||||
if (el != element) {
|
labels << symbol.label;
|
||||||
labels << el->elementInformations().value(QETInformation::ELMT_LABEL).toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
labels.remove(QString());
|
labels.remove(QString());
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#ifndef ELEMENTAUTONUMSCHEMECOMMAND_H
|
#ifndef ELEMENTAUTONUMSCHEMECOMMAND_H
|
||||||
#define ELEMENTAUTONUMSCHEMECOMMAND_H
|
#define ELEMENTAUTONUMSCHEMECOMMAND_H
|
||||||
|
|
||||||
|
#include "assignvariables.h"
|
||||||
#include "numerotationcontext.h"
|
#include "numerotationcontext.h"
|
||||||
#include "renumberelementscommand.h"
|
#include "renumberelementscommand.h"
|
||||||
|
|
||||||
@@ -201,6 +202,8 @@ class ElementAutoNumSchemeCommand : public QUndoCommand
|
|||||||
static QSet<QString> labelsHeldBesides(const QETProject *project, const Element *element);
|
static QSet<QString> labelsHeldBesides(const QETProject *project, const Element *element);
|
||||||
static NumberSupport numberSupport(const NumerotationContext &context);
|
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 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<GapRange> gapRanges(const QETProject *project, const QString &title);
|
||||||
static QList<int> freeNumbers(const QETProject *project, const QString &title,
|
static QList<int> freeNumbers(const QETProject *project, const QString &title,
|
||||||
const Element *element, int limit = 500);
|
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) {
|
if (ui->m_from_folios_cb->count()<=0) {
|
||||||
ui->m_from_folios_cb->clear();
|
ui->m_from_folios_cb->clear();
|
||||||
ui->m_from_folios_cb->addItem("");
|
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() != "")
|
if (diagram->title() != "")
|
||||||
ui->m_from_folios_cb->addItem(
|
ui->m_from_folios_cb->addItem(
|
||||||
diagram->title(),
|
diagram->title(),
|
||||||
@@ -206,16 +207,17 @@ void AutoNumberingManagementW::on_m_from_folios_cb_currentIndexChanged(
|
|||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
ui->m_to_folios_cb->setEnabled(true);
|
ui->m_to_folios_cb->setEnabled(true);
|
||||||
ui->m_to_folios_cb->addItem("");
|
ui->m_to_folios_cb->addItem("");
|
||||||
for (int i=index;i<project_->diagrams().size();i++) {
|
const QList<Diagram *> folios = project_->folios();
|
||||||
if (project_->diagrams().at(i)->title() != "") {
|
for (int i=index;i<folios.size();i++) {
|
||||||
|
if (folios.at(i)->title() != "") {
|
||||||
ui->m_to_folios_cb->addItem(
|
ui->m_to_folios_cb->addItem(
|
||||||
project_->diagrams().at(i)->title(),
|
folios.at(i)->title(),
|
||||||
project_->diagrams().at(i)->folioIndex());
|
folios.at(i)->folioIndex());
|
||||||
}
|
}
|
||||||
else ui->m_to_folios_cb->addItem(
|
else ui->m_to_folios_cb->addItem(
|
||||||
QString::number(
|
QString::number(
|
||||||
project_->diagrams().at(i)->folioIndex()),
|
folios.at(i)->folioIndex()),
|
||||||
project_->diagrams().at(i)->folioIndex());
|
folios.at(i)->folioIndex());
|
||||||
}
|
}
|
||||||
applyEnable(true);
|
applyEnable(true);
|
||||||
ui->m_selected_folios_le->clear();
|
ui->m_selected_folios_le->clear();
|
||||||
|
|||||||
@@ -136,7 +136,8 @@ void FolioAutonumberingW::on_m_autonumber_tabs_rb_clicked()
|
|||||||
if (ui->m_from_cb->count()<=0){
|
if (ui->m_from_cb->count()<=0){
|
||||||
ui->m_from_cb->clear();
|
ui->m_from_cb->clear();
|
||||||
ui->m_from_cb->addItem("");
|
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());
|
ui->m_from_cb->addItem(diagram->title());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,9 +165,9 @@ void FolioAutonumberingW::on_m_from_cb_currentIndexChanged(int)
|
|||||||
ui->m_to_cb->clear();
|
ui->m_to_cb->clear();
|
||||||
if (index > 0){
|
if (index > 0){
|
||||||
ui->m_to_cb->setEnabled(true);
|
ui->m_to_cb->setEnabled(true);
|
||||||
for (int i=index;i<project_->diagrams().size();i++)
|
const QList<Diagram *> folios = project_->folios();
|
||||||
ui->m_to_cb->addItem(project_
|
for (int i=index;i<folios.size();i++)
|
||||||
->diagrams().at(i)->title());
|
ui->m_to_cb->addItem(folios.at(i)->title());
|
||||||
applyEnable(true);
|
applyEnable(true);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
|||||||
+113
-23
@@ -29,6 +29,10 @@
|
|||||||
#include "titleblocktemplaterenderer.h"
|
#include "titleblocktemplaterenderer.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "diagram.h"
|
||||||
|
#include "qetproject.h"
|
||||||
|
#include "dataBase/projectdatabase.h"
|
||||||
|
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
@@ -282,28 +286,98 @@ void BorderTitleBlock::borderFromXml(const QDomElement &xml_elmt) {
|
|||||||
@return the properties of the titleblock
|
@return the properties of the titleblock
|
||||||
\~French les proprietes du cartouches
|
\~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;
|
TitleBlockProperties ip;
|
||||||
|
|
||||||
ip.author = author();
|
ip.author = btb_author_;
|
||||||
ip.date = date();
|
ip.date = btb_date_;
|
||||||
ip.title = title();
|
ip.title = btb_title_;
|
||||||
ip.filename = fileName();
|
ip.filename = btb_filename_;
|
||||||
ip.plant = plant();
|
ip.plant = btb_plant_;
|
||||||
ip.locmach = locmach();
|
ip.locmach = btb_locmach_;
|
||||||
ip.indexrev = indexrev();
|
ip.indexrev = btb_indexrev_;
|
||||||
ip.version = version();
|
ip.version = btb_version_;
|
||||||
ip.folio = folio();
|
ip.folio = btb_folio_;
|
||||||
ip.template_name = titleBlockTemplateName();
|
ip.template_name = titleBlockTemplateName();
|
||||||
ip.display_at = m_edge;
|
ip.display_at = m_edge;
|
||||||
ip.auto_page_num = autoPageNum();
|
ip.auto_page_num = btb_auto_page_num_;
|
||||||
ip.context = additional_fields_;
|
ip.context = additional_fields_;
|
||||||
ip.collection = QET::QetCollection::Embedded;
|
ip.collection = QET::QetCollection::Embedded;
|
||||||
|
|
||||||
return(ip);
|
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
|
@brief BorderTitleBlock::importTitleBlock
|
||||||
@param ip the new properties of titleblock
|
@param ip the new properties of titleblock
|
||||||
@@ -333,6 +407,7 @@ void BorderTitleBlock::importTitleBlock(const TitleBlockProperties &ip)
|
|||||||
// through setFolioData(),
|
// through setFolioData(),
|
||||||
// which in turn calls updateDiagramContextForTitleBlock().
|
// which in turn calls updateDiagramContextForTitleBlock().
|
||||||
emit(needTitleBlockTemplate(ip.template_name));
|
emit(needTitleBlockTemplate(ip.template_name));
|
||||||
|
storeTitleBlock();
|
||||||
emit informationChanged();
|
emit informationChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,6 +466,7 @@ void BorderTitleBlock::setTitleBlockTemplate(
|
|||||||
const TitleBlockTemplate *titleblock_template) {
|
const TitleBlockTemplate *titleblock_template) {
|
||||||
m_titleblock_template_renderer -> setTitleBlockTemplate(
|
m_titleblock_template_renderer -> setTitleBlockTemplate(
|
||||||
titleblock_template);
|
titleblock_template);
|
||||||
|
storeTitleBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -889,9 +965,21 @@ DiagramPosition BorderTitleBlock::convertPosition(const QPointF &pos)
|
|||||||
void BorderTitleBlock::setFolio(const QString &folio)
|
void BorderTitleBlock::setFolio(const QString &folio)
|
||||||
{
|
{
|
||||||
btb_folio_ = folio;
|
btb_folio_ = folio;
|
||||||
|
storeTitleBlock();
|
||||||
emit (titleBlockFolioChanged(folio));
|
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
|
@brief BorderTitleBlock::updateDiagramContextForTitleBlock
|
||||||
Update the informations given to the titleblock template
|
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
|
// 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
|
// one is already there to show through; otherwise it still goes in
|
||||||
// empty, which is what makes the placeholder resolve to nothing.
|
// empty, which is what makes the placeholder resolve to nothing.
|
||||||
|
const TitleBlockProperties tb = current();
|
||||||
DiagramContext context = initial_context;
|
DiagramContext context = initial_context;
|
||||||
foreach (QString key, additional_fields_.keys()) {
|
foreach (QString key, tb.context.keys()) {
|
||||||
const QVariant value = additional_fields_[key];
|
const QVariant value = tb.context[key];
|
||||||
if (!value.toString().isEmpty() || !context.contains(key))
|
if (!value.toString().isEmpty() || !context.contains(key))
|
||||||
context.addValue(key, value);
|
context.addValue(key, value);
|
||||||
}
|
}
|
||||||
@@ -925,20 +1014,20 @@ void BorderTitleBlock::updateDiagramContextForTitleBlock(
|
|||||||
// ... overridden by the historical and/or dynamically generated fields
|
// ... overridden by the historical and/or dynamically generated fields
|
||||||
QLocale var;
|
QLocale var;
|
||||||
var.dateFormat(QLocale::ShortFormat);
|
var.dateFormat(QLocale::ShortFormat);
|
||||||
context.addValue("author", btb_author_);
|
context.addValue("author", tb.author);
|
||||||
context.addValue(
|
context.addValue(
|
||||||
"date",
|
"date",
|
||||||
QLocale::system().toString(btb_date_, QLocale::ShortFormat));
|
QLocale::system().toString(tb.date, QLocale::ShortFormat));
|
||||||
context.addValue("title", btb_title_);
|
context.addValue("title", tb.title);
|
||||||
context.addValue("filename", btb_filename_);
|
context.addValue("filename", tb.filename);
|
||||||
context.addValue("plant", btb_plant_);
|
context.addValue("plant", tb.plant);
|
||||||
context.addValue("locmach", btb_locmach_);
|
context.addValue("locmach", tb.locmach);
|
||||||
context.addValue("indexrev", btb_indexrev_);
|
context.addValue("indexrev", tb.indexrev);
|
||||||
context.addValue("version", btb_version_);
|
context.addValue("version", tb.version);
|
||||||
context.addValue("folio", btb_final_folio_);
|
context.addValue("folio", btb_final_folio_);
|
||||||
context.addValue("folio-id", folio_index_);
|
context.addValue("folio-id", folio_index_);
|
||||||
context.addValue("folio-total", folio_total_);
|
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("previous-folio-num", m_previous_folio_num);
|
||||||
context.addValue("next-folio-num", m_next_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
|
// regenerate the content of the folio field
|
||||||
// regenere le contenu du champ folio
|
// regenere le contenu du champ folio
|
||||||
btb_final_folio_ = btb_folio_;
|
btb_final_folio_ = folio();
|
||||||
|
|
||||||
if (btb_final_folio_.contains("%autonum")){
|
if (btb_final_folio_.contains("%autonum")){
|
||||||
btb_final_folio_.replace("%autonum", autonum);
|
btb_final_folio_.replace("%autonum", autonum);
|
||||||
btb_folio_ = btb_final_folio_;
|
btb_folio_ = btb_final_folio_;
|
||||||
|
storeTitleBlock();
|
||||||
}
|
}
|
||||||
btb_final_folio_.replace("%id", QString::number(folio_index_));
|
btb_final_folio_.replace("%id", QString::number(folio_index_));
|
||||||
btb_final_folio_.replace("%total", QString::number(folio_total_));
|
btb_final_folio_.replace("%total", QString::number(folio_total_));
|
||||||
|
|||||||
+25
-14
@@ -29,6 +29,8 @@ class QPainter;
|
|||||||
class DiagramPosition;
|
class DiagramPosition;
|
||||||
class TitleBlockTemplate;
|
class TitleBlockTemplate;
|
||||||
class TitleBlockTemplateRenderer;
|
class TitleBlockTemplateRenderer;
|
||||||
|
class Diagram;
|
||||||
|
class projectDataBase;
|
||||||
/**
|
/**
|
||||||
@brief The BorderTitleBlock class
|
@brief The BorderTitleBlock class
|
||||||
This class represents the border and the titleblock which frame a
|
This class represents the border and the titleblock which frame a
|
||||||
@@ -107,34 +109,36 @@ class BorderTitleBlock : public QObject
|
|||||||
QRectF outsideBorderRect() const;
|
QRectF outsideBorderRect() const;
|
||||||
QRectF insideBorderRect() 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
|
/// @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
|
/// @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
|
/// @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
|
/// @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
|
/// @return the value of the title block "Folio" field as displayed
|
||||||
QString finalfolio() const { return(btb_final_folio_); }
|
QString finalfolio() const { return(btb_final_folio_); }
|
||||||
/// @return the value of the title block "Plant" field
|
/// @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
|
/// @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
|
/// @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
|
/// @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
|
/// @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
|
/// @return the value of the title block Additional Fields
|
||||||
DiagramContext additionalFields() const {
|
DiagramContext additionalFields() const {
|
||||||
return (additional_fields_); }
|
const TitleBlockProperties *s = stored(); return(s ? s->context : additional_fields_); }
|
||||||
/// @return the value of the title block
|
/// @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
|
/// 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
|
/// @return the value of the total number of folios
|
||||||
int folioTotal() const { return(folio_total_);}
|
int folioTotal() const { return(folio_total_);}
|
||||||
|
|
||||||
@@ -175,7 +179,9 @@ class BorderTitleBlock : public QObject
|
|||||||
void borderToXml(QDomElement &);
|
void borderToXml(QDomElement &);
|
||||||
void borderFromXml(const QDomElement &);
|
void borderFromXml(const QDomElement &);
|
||||||
|
|
||||||
TitleBlockProperties exportTitleBlock();
|
TitleBlockProperties exportTitleBlock() const;
|
||||||
|
TitleBlockProperties localTitleBlock() const;
|
||||||
|
void setOwnerFolio(Diagram *folio);
|
||||||
void importTitleBlock(const TitleBlockProperties &);
|
void importTitleBlock(const TitleBlockProperties &);
|
||||||
BorderProperties exportBorder();
|
BorderProperties exportBorder();
|
||||||
void importBorder(const BorderProperties &);
|
void importBorder(const BorderProperties &);
|
||||||
@@ -289,5 +295,10 @@ class BorderTitleBlock : public QObject
|
|||||||
bool display_rows_ = false;
|
bool display_rows_ = false;
|
||||||
bool display_border_ = false;
|
bool display_border_ = false;
|
||||||
TitleBlockTemplateRenderer *m_titleblock_template_renderer;
|
TitleBlockTemplateRenderer *m_titleblock_template_renderer;
|
||||||
|
Diagram *m_owner_folio = nullptr;
|
||||||
|
TitleBlockProperties current() const;
|
||||||
|
const TitleBlockProperties *stored() const;
|
||||||
|
projectDataBase *placedStore() const;
|
||||||
|
void storeTitleBlock();
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -335,6 +335,10 @@ void CableManager::refreshLabels(QETProject *project)
|
|||||||
QString label;
|
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;
|
QHash<QUuid, Binding> bindings;
|
||||||
for (Cable *cable : project->cables())
|
for (Cable *cable : project->cables())
|
||||||
{
|
{
|
||||||
@@ -361,7 +365,7 @@ void CableManager::refreshLabels(QETProject *project)
|
|||||||
//the core or the link does.
|
//the core or the link does.
|
||||||
QHash<QUuid, Conductor *> conductors_by_uuid;
|
QHash<QUuid, Conductor *> conductors_by_uuid;
|
||||||
QHash<Element *, QList<Conductor *>> endings_on_report;
|
QHash<Element *, QList<Conductor *>> endings_on_report;
|
||||||
for (Diagram *diagram : project->diagrams())
|
for (Diagram *diagram : folios)
|
||||||
{
|
{
|
||||||
if (!diagram) continue;
|
if (!diagram) continue;
|
||||||
for (Conductor *conductor : diagram->conductors())
|
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;
|
if (!diagram) continue;
|
||||||
for (Conductor *conductor : diagram->conductors())
|
for (Conductor *conductor : diagram->conductors())
|
||||||
|
|||||||
+12
-4
@@ -869,8 +869,8 @@ int exportLinks(QETProject &project, const QString &output)
|
|||||||
/// diff can reveal markup QET silently normalises (tolerated-but-invalid XML).
|
/// diff can reveal markup QET silently normalises (tolerated-but-invalid XML).
|
||||||
int resaveProject(QETProject &project, const QString &output)
|
int resaveProject(QETProject &project, const QString &output)
|
||||||
{
|
{
|
||||||
const QDomDocument doc = project.toXml();
|
|
||||||
if (QETProject::isContainerPath(output)) {
|
if (QETProject::isContainerPath(output)) {
|
||||||
|
const QDomDocument doc = project.toXml();
|
||||||
QString error;
|
QString error;
|
||||||
if (!QETProject::writeContainer(doc, output, &error)) {
|
if (!QETProject::writeContainer(doc, output, &error)) {
|
||||||
err << "Cannot write '" << output << "': " << error << "\n";
|
err << "Cannot write '" << output << "': " << error << "\n";
|
||||||
@@ -879,13 +879,17 @@ int resaveProject(QETProject &project, const QString &output)
|
|||||||
out << "Re-saved project -> " << output << "\n";
|
out << "Re-saved project -> " << output << "\n";
|
||||||
return 0;
|
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);
|
QFile file(output);
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
err << "Cannot open '" << output << "' for writing.\n";
|
err << "Cannot open '" << output << "' for writing.\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
QTextStream fout(&file);
|
QTextStream fout(&file);
|
||||||
fout << doc.toString(4);
|
fout << QETProject::spliceKeptFolios(doc.toString(4), kept_folios);
|
||||||
file.close();
|
file.close();
|
||||||
out << "Re-saved project -> " << output << "\n";
|
out << "Re-saved project -> " << output << "\n";
|
||||||
return 0;
|
return 0;
|
||||||
@@ -963,21 +967,25 @@ int setTitleBlock(QETProject &project, const QString &output,
|
|||||||
++folios;
|
++folios;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QDomDocument doc = project.toXml();
|
|
||||||
if (QETProject::isContainerPath(output)) {
|
if (QETProject::isContainerPath(output)) {
|
||||||
|
const QDomDocument doc = project.toXml();
|
||||||
QString error;
|
QString error;
|
||||||
if (!QETProject::writeContainer(doc, output, &error)) {
|
if (!QETProject::writeContainer(doc, output, &error)) {
|
||||||
err << "Cannot write '" << output << "': " << error << "\n";
|
err << "Cannot write '" << output << "': " << error << "\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
QFile file(output);
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
err << "Cannot open '" << output << "' for writing.\n";
|
err << "Cannot open '" << output << "' for writing.\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
QTextStream fout(&file);
|
QTextStream fout(&file);
|
||||||
fout << doc.toString(4);
|
fout << QETProject::spliceKeptFolios(doc.toString(4), kept_folios);
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
out << "Stamped " << fields.size() << " field(s) on "
|
out << "Stamped " << fields.size() << " field(s) on "
|
||||||
|
|||||||
@@ -217,10 +217,22 @@ void SingleLineProperties::drawPen(QPainter *painter,
|
|||||||
*/
|
*/
|
||||||
void SingleLineProperties::toXml(QDomElement &e) const
|
void SingleLineProperties::toXml(QDomElement &e) const
|
||||||
{
|
{
|
||||||
e.setAttribute("ground", hasGround ? "true" : "false");
|
for (const auto &attribute : attributes())
|
||||||
e.setAttribute("neutral", hasNeutral ? "true" : "false");
|
e.setAttribute(attribute.first, attribute.second);
|
||||||
e.setAttribute("phase", phases);
|
}
|
||||||
if (isPen()) e.setAttribute("pen", "true");
|
|
||||||
|
/**
|
||||||
|
@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
|
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))
|
if (color != QColor(Qt::black))
|
||||||
e.setAttribute("color", color.name());
|
add("color", color.name());
|
||||||
|
|
||||||
e.setAttribute("bicolor", m_bicolor? "true" : "false");
|
add("bicolor", m_bicolor? "true" : "false");
|
||||||
e.setAttribute("color2", m_color_2.name());
|
add("color2", m_color_2.name());
|
||||||
e.setAttribute("dash-size", QString::number(m_dash_size));
|
add("dash-size", QString::number(m_dash_size));
|
||||||
|
|
||||||
if (type == Single)
|
if (type == Single)
|
||||||
singleLineProperties.toXml(e);
|
a << singleLineProperties.attributes();
|
||||||
|
|
||||||
e.setAttribute("num", text);
|
add("num", text);
|
||||||
e.setAttribute("text_color", text_color.name());
|
add("text_color", text_color.name());
|
||||||
e.setAttribute("formula", m_formula);
|
add("formula", m_formula);
|
||||||
e.setAttribute("cable", m_cable);
|
add("cable", m_cable);
|
||||||
if (!m_cable_uuid.isNull())
|
if (!m_cable_uuid.isNull())
|
||||||
{
|
{
|
||||||
//Only written when there is a cable: a conductor which is no
|
//Only written when there is a cable: a conductor which is no
|
||||||
//part of one keeps whatever cable text it had and no reference
|
//part of one keeps whatever cable text it had and no reference
|
||||||
//at all, so a hand written value is never mistaken for a
|
//at all, so a hand written value is never mistaken for a
|
||||||
//generated one.
|
//generated one.
|
||||||
e.setAttribute("cable_uuid", m_cable_uuid.toString());
|
add("cable_uuid", m_cable_uuid.toString());
|
||||||
e.setAttribute("cable_slot", QString::number(m_cable_slot));
|
add("cable_slot", QString::number(m_cable_slot));
|
||||||
}
|
}
|
||||||
e.setAttribute("bus", m_bus);
|
add("bus", m_bus);
|
||||||
e.setAttribute("function", m_function);
|
add("function", m_function);
|
||||||
e.setAttribute("tension_protocol", m_tension_protocol);
|
add("tension_protocol", m_tension_protocol);
|
||||||
e.setAttribute("conductor_color", m_wire_color);
|
add("conductor_color", m_wire_color);
|
||||||
e.setAttribute("conductor_section", m_wire_section);
|
add("conductor_section", m_wire_section);
|
||||||
e.setAttribute("numsize", QString::number(text_size));
|
add("numsize", QString::number(text_size));
|
||||||
e.setAttribute("condsize", QString::number(cond_size));
|
add("condsize", QString::number(cond_size));
|
||||||
e.setAttribute("displaytext", m_show_text);
|
add("displaytext", QString::number(m_show_text));
|
||||||
e.setAttribute("onetextperfolio", m_one_text_per_folio);
|
add("onetextperfolio", QString::number(m_one_text_per_folio));
|
||||||
e.setAttribute("vertirotatetext", QString::number(verti_rotate_text));
|
add("vertirotatetext", QString::number(verti_rotate_text));
|
||||||
e.setAttribute("horizrotatetext", QString::number(horiz_rotate_text));
|
add("horizrotatetext", QString::number(horiz_rotate_text));
|
||||||
|
|
||||||
QMetaEnum me = QMetaEnum::fromType<Qt::Alignment>();
|
QMetaEnum me = QMetaEnum::fromType<Qt::Alignment>();
|
||||||
e.setAttribute("horizontal-alignment", me.valueToKey(m_horizontal_alignment));
|
add("horizontal-alignment", QString::fromLatin1(me.valueToKey(m_horizontal_alignment)));
|
||||||
e.setAttribute("vertical-alignment", me.valueToKey(m_vertical_alignment));
|
add("vertical-alignment", QString::fromLatin1(me.valueToKey(m_vertical_alignment)));
|
||||||
|
|
||||||
QString conductor_style = writeStyle();
|
QString conductor_style = writeStyle();
|
||||||
if (!conductor_style.isEmpty())
|
if (!conductor_style.isEmpty())
|
||||||
e.setAttribute("style", conductor_style);
|
add("style", conductor_style);
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class SingleLineProperties {
|
|||||||
bool isPen() const;
|
bool isPen() const;
|
||||||
void draw(QPainter *, QET::ConductorSegmentType, const QRectF &);
|
void draw(QPainter *, QET::ConductorSegmentType, const QRectF &);
|
||||||
void toXml(QDomElement &) const;
|
void toXml(QDomElement &) const;
|
||||||
|
QList<QPair<QString, QString>> attributes() const;
|
||||||
void fromXml(QDomElement &);
|
void fromXml(QDomElement &);
|
||||||
void toSettings(QSettings &, const QString & = QString()) const;
|
void toSettings(QSettings &, const QString & = QString()) const;
|
||||||
void fromSettings(QSettings &, const QString & = QString());
|
void fromSettings(QSettings &, const QString & = QString());
|
||||||
@@ -130,6 +131,7 @@ class ConductorProperties
|
|||||||
|
|
||||||
// methods
|
// methods
|
||||||
void toXml(QDomElement &) const;
|
void toXml(QDomElement &) const;
|
||||||
|
QList<QPair<QString, QString>> attributes() const;
|
||||||
void fromXml(QDomElement &);
|
void fromXml(QDomElement &);
|
||||||
void toSettings(QSettings &, const QString & = QString()) const;
|
void toSettings(QSettings &, const QString & = QString()) const;
|
||||||
void fromSettings(QSettings &, const QString & = QString());
|
void fromSettings(QSettings &, const QString & = QString());
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -24,9 +24,15 @@
|
|||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QDomDocument>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
|
#include "../diagramcontext.h"
|
||||||
|
#include "../titleblockproperties.h"
|
||||||
|
#include "../conductorproperties.h"
|
||||||
|
|
||||||
class Element;
|
class Element;
|
||||||
class QETProject;
|
class QETProject;
|
||||||
class Diagram;
|
class Diagram;
|
||||||
@@ -98,6 +104,62 @@ class projectDataBase : public QObject
|
|||||||
QSqlDatabase database() const {return m_data_base;}
|
QSqlDatabase database() const {return m_data_base;}
|
||||||
int excludedConductorCount() const;
|
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 addElement (Element *element);
|
||||||
void removeElement (Element *element);
|
void removeElement (Element *element);
|
||||||
void elementInfoChanged (Element *element);
|
void elementInfoChanged (Element *element);
|
||||||
@@ -145,6 +207,7 @@ class projectDataBase : public QObject
|
|||||||
void flushElementPositions();
|
void flushElementPositions();
|
||||||
void populateElementTable();
|
void populateElementTable();
|
||||||
void populateElementInfoTable();
|
void populateElementInfoTable();
|
||||||
|
void populateElementInfoTableFromFolios();
|
||||||
void populateDiagramInfoTable();
|
void populateDiagramInfoTable();
|
||||||
void populateConductorTable();
|
void populateConductorTable();
|
||||||
void populateLinkTable();
|
void populateLinkTable();
|
||||||
@@ -203,7 +266,12 @@ class projectDataBase : public QObject
|
|||||||
m_cascade_remove_element_query,
|
m_cascade_remove_element_query,
|
||||||
m_insert_shape_query,
|
m_insert_shape_query,
|
||||||
m_insert_independent_text_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
|
//Which uuid's row each drawing item last wrote, and which item
|
||||||
//wrote each row. A pasted copy is added to the folio still
|
//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
|
//sends xChanged and yChanged for every step of a drag, so the
|
||||||
//cell is written when next read, as drawing items are.
|
//cell is written when next read, as drawing items are.
|
||||||
QList<QPointer<Element>> m_moved_elements;
|
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
|
#ifdef QET_EXPORT_PROJECT_DB
|
||||||
public:
|
public:
|
||||||
|
|||||||
+256
-161
@@ -16,6 +16,7 @@
|
|||||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "diagram.h"
|
#include "diagram.h"
|
||||||
|
#include "dataBase/projectdatabase.h"
|
||||||
#include "autoNum/elementautonumschemecommand.h"
|
#include "autoNum/elementautonumschemecommand.h"
|
||||||
|
|
||||||
#include "ElementsCollection/elementcollectionhandler.h"
|
#include "ElementsCollection/elementcollectionhandler.h"
|
||||||
@@ -193,6 +194,7 @@ Diagram::Diagram(QETProject *project) :
|
|||||||
* http://tech-artists.org/t/qt-properly-removing-qgraphicitems/3063
|
* http://tech-artists.org/t/qt-properly-removing-qgraphicitems/3063
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
border_and_titleblock.setOwnerFolio(this);
|
||||||
qgi_manager_ = new QGIManager(this);
|
qgi_manager_ = new QGIManager(this);
|
||||||
setBackgroundBrush(Qt::white);
|
setBackgroundBrush(Qt::white);
|
||||||
conductor_setter_ = new QGraphicsLineItem(nullptr);
|
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
|
@brief Diagram::uuid
|
||||||
@return the uuid of this diagram
|
@return the uuid of this diagram
|
||||||
@@ -801,7 +846,7 @@ bool Diagram::uuidUsedByOtherDiagram(const QUuid &uuid) const
|
|||||||
if (!m_project) {
|
if (!m_project) {
|
||||||
return false;
|
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) {
|
for (const Diagram *diagram : diagrams) {
|
||||||
if (diagram != this && diagram->m_uuid == uuid) {
|
if (diagram != this && diagram->m_uuid == uuid) {
|
||||||
return true;
|
return true;
|
||||||
@@ -838,7 +883,7 @@ QUuid Diagram::derivedUuid(const QDomElement &root, const QString &reason) const
|
|||||||
QStringLiteral("{d5951240-154d-44d6-8277-0092a31d1920}"));
|
QStringLiteral("{d5951240-154d-44d6-8277-0092a31d1920}"));
|
||||||
|
|
||||||
const int index = m_project
|
const int index = m_project
|
||||||
? m_project->diagrams().indexOf(const_cast<Diagram *>(this))
|
? m_project->folios().indexOf(const_cast<Diagram *>(this))
|
||||||
: -1;
|
: -1;
|
||||||
const QString project_title = root.ownerDocument()
|
const QString project_title = root.ownerDocument()
|
||||||
.documentElement()
|
.documentElement()
|
||||||
@@ -1088,6 +1133,10 @@ QList < QSet <Conductor *> > Diagram::potentials()
|
|||||||
\~French Un Document XML (QDomDocument)
|
\~French Un Document XML (QDomDocument)
|
||||||
*/
|
*/
|
||||||
QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
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
|
// document
|
||||||
QDomDocument document;
|
QDomDocument document;
|
||||||
|
|
||||||
@@ -1098,6 +1147,185 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
|||||||
// schema properties
|
// schema properties
|
||||||
// proprietes du schema
|
// proprietes du schema
|
||||||
if (whole_content) {
|
if (whole_content) {
|
||||||
|
writeHeader(document, dom_root);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//this method with whole_content to false,
|
||||||
|
//is often use to copy and paste the current selection
|
||||||
|
//so we add the id of the project where copy occur.
|
||||||
|
dom_root.setAttribute(QStringLiteral("projectId"), QETApp::projectId(m_project));
|
||||||
|
}
|
||||||
|
document.appendChild(dom_root);
|
||||||
|
|
||||||
|
if (items().isEmpty())
|
||||||
|
return(document);
|
||||||
|
|
||||||
|
QVector<Element *> list_elements;
|
||||||
|
QVector<Conductor *> list_conductors;
|
||||||
|
QVector<DiagramTextItem *> list_texts;
|
||||||
|
QVector<DiagramImageItem *> list_images;
|
||||||
|
QVector<QetShapeItem *> list_shapes;
|
||||||
|
QVector<QetGraphicsTableItem *> table_vector;
|
||||||
|
QVector<TerminalStripItem *> strip_vector;
|
||||||
|
|
||||||
|
//Ckeck graphics item to "XMLise"
|
||||||
|
for(QGraphicsItem *qgi : items())
|
||||||
|
{
|
||||||
|
switch (qgi->type())
|
||||||
|
{
|
||||||
|
case Element::Type: {
|
||||||
|
auto elmt = static_cast<Element *>(qgi);
|
||||||
|
if (whole_content || elmt->isSelected()){
|
||||||
|
// For a copy/paste command, the text positions must be recalculated for
|
||||||
|
// correct text alignment, before it is saved to the clipboard
|
||||||
|
if(is_copy_command && (elmt->linkType() == Element::Slave || elmt->linkType()&Element::AllReport))
|
||||||
|
correctTextPos(elmt);
|
||||||
|
list_elements << elmt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Conductor::Type: {
|
||||||
|
auto cond = static_cast<Conductor *>(qgi);
|
||||||
|
if (whole_content)
|
||||||
|
list_conductors << cond;
|
||||||
|
/* When we did not export the whole diagram,
|
||||||
|
* we must remove the non-selected conductors.
|
||||||
|
* At this step that means a conductor which one
|
||||||
|
* of these two element are not selected
|
||||||
|
*/
|
||||||
|
else if (cond->terminal1->parentItem()->isSelected()
|
||||||
|
&& cond->terminal2->parentItem()->isSelected())
|
||||||
|
list_conductors << cond;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DiagramImageItem::Type: {
|
||||||
|
auto image = static_cast<DiagramImageItem *>(qgi);
|
||||||
|
if (whole_content || image->isSelected())
|
||||||
|
list_images << image;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IndependentTextItem::Type: {
|
||||||
|
auto indi_text = static_cast<IndependentTextItem *>(qgi);
|
||||||
|
if (whole_content || indi_text->isSelected())
|
||||||
|
list_texts << indi_text;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QetShapeItem::Type: {
|
||||||
|
auto shape = static_cast<QetShapeItem *>(qgi);
|
||||||
|
if (whole_content || shape->isSelected())
|
||||||
|
list_shapes << shape;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QetGraphicsTableItem::Type: {
|
||||||
|
auto table = static_cast<QetGraphicsTableItem *>(qgi);
|
||||||
|
if (whole_content || table->isSelected())
|
||||||
|
table_vector << table;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TerminalStripItem::Type: {
|
||||||
|
const auto strip = static_cast<TerminalStripItem *>(qgi);
|
||||||
|
if (whole_content || strip->isSelected()) {
|
||||||
|
strip_vector << strip;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// items() returns items in stacking order, which is not guaranteed
|
||||||
|
// reproducible across processes (ties between same-Z items follow
|
||||||
|
// the scene's internal index, not any content-derived order) -- so
|
||||||
|
// without this, saving an unmodified project produces a different
|
||||||
|
// byte stream on every run. Elements and conductors are the two
|
||||||
|
// blocks observed to actually churn across the example corpus;
|
||||||
|
// sort them into a deterministic, content-derived order before
|
||||||
|
// serializing. This also fixes the legacy terminal-id churn below,
|
||||||
|
// 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) {
|
||||||
|
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
|
||||||
|
// them (see PasteNumberingImport)
|
||||||
|
if (is_copy_command) {
|
||||||
|
ElementAutoNumSchemeCommand::writeCopiedSchemes(document, dom_root, m_project, list_elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
// correspondence table between the addresses of the terminals and their ids
|
||||||
|
// table de correspondance entre les adresses des bornes et leurs ids
|
||||||
|
QHash<Terminal *, int> table_adr_id;
|
||||||
|
|
||||||
|
if (!list_elements.isEmpty()) {
|
||||||
|
auto dom_elements = document.createElement(QStringLiteral("elements"));
|
||||||
|
for (auto elmt : list_elements) {
|
||||||
|
QDomElement dom_element = elmt->toXml(document, table_adr_id);
|
||||||
|
ItemGroups::write(dom_element, elmt);
|
||||||
|
dom_elements.appendChild(dom_element);
|
||||||
|
// If copy is active we have to undo the changes we have made during creating(filling) 'list_elements'
|
||||||
|
if(is_copy_command && (elmt->linkType() == Element::Slave || elmt->linkType()&Element::AllReport))
|
||||||
|
restoreText(elmt);
|
||||||
|
}
|
||||||
|
dom_root.appendChild(dom_elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list_conductors.isEmpty()) {
|
||||||
|
//Symbols copied in old versions can share one uuid. A wire
|
||||||
|
//saved by that uuid would reopen on the first of them (#1408).
|
||||||
|
QSet<QUuid> seen_uuids, shared_uuids;
|
||||||
|
for (auto elmt : std::as_const(list_elements)) {
|
||||||
|
if (seen_uuids.contains(elmt->uuid()))
|
||||||
|
shared_uuids.insert(elmt->uuid());
|
||||||
|
seen_uuids.insert(elmt->uuid());
|
||||||
|
}
|
||||||
|
auto dom_conductors = document.createElement(QStringLiteral("conductors"));
|
||||||
|
for (auto cond : list_conductors) {
|
||||||
|
dom_conductors.appendChild(cond->toXml(document,
|
||||||
|
table_adr_id,
|
||||||
|
shared_uuids));
|
||||||
|
}
|
||||||
|
dom_root.appendChild(dom_conductors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A rect query, unlike items(), returns true stacking order (z, then
|
||||||
|
// insertion order) even with NoIndex.
|
||||||
|
QHash<const QGraphicsItem *, int> stack_rank;
|
||||||
|
{
|
||||||
|
const QList<QGraphicsItem *> stacked = items(
|
||||||
|
QRectF(-1e9, -1e9, 2e9, 2e9), Qt::IntersectsItemBoundingRect,
|
||||||
|
Qt::AscendingOrder);
|
||||||
|
for (int i = 0 ; i < stacked.size() ; ++i)
|
||||||
|
stack_rank.insert(stacked.at(i), i);
|
||||||
|
}
|
||||||
|
appendInStackingOrder(document, dom_root, QStringLiteral("inputs"), list_texts, stack_rank);
|
||||||
|
appendInStackingOrder(document, dom_root, QStringLiteral("images"), list_images, stack_rank);
|
||||||
|
appendInStackingOrder(document, dom_root, QStringLiteral("shapes"), list_shapes, stack_rank);
|
||||||
|
appendInStackingOrder(document, dom_root, QStringLiteral("tables"), table_vector, stack_rank);
|
||||||
|
|
||||||
|
if (!strip_vector.isEmpty()) {
|
||||||
|
dom_root.appendChild(TerminalStripItemXml::toXml(strip_vector, document));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
//Persist the folio identity, so that a folio keeps the same uuid
|
||||||
//across save/load. Without it every load invents a new one, and
|
//across save/load. Without it every load invents a new one, and
|
||||||
//nothing outside the running instance (version control, a lock,
|
//nothing outside the running instance (version control, a lock,
|
||||||
@@ -1213,166 +1441,18 @@ QDomDocument Diagram::toXml(bool whole_content, bool is_copy_command) {
|
|||||||
dom_root.appendChild(cndfoliosequential);
|
dom_root.appendChild(cndfoliosequential);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
//this method with whole_content to false,
|
|
||||||
//is often use to copy and paste the current selection
|
|
||||||
//so we add the id of the project where copy occur.
|
|
||||||
dom_root.setAttribute(QStringLiteral("projectId"), QETApp::projectId(m_project));
|
|
||||||
}
|
|
||||||
document.appendChild(dom_root);
|
|
||||||
|
|
||||||
if (items().isEmpty())
|
/**
|
||||||
return(document);
|
@brief Diagram::headerXml
|
||||||
|
@return the folio's own data as writeHeader() writes it, as text
|
||||||
QVector<Element *> list_elements;
|
|
||||||
QVector<Conductor *> list_conductors;
|
|
||||||
QVector<DiagramTextItem *> list_texts;
|
|
||||||
QVector<DiagramImageItem *> list_images;
|
|
||||||
QVector<QetShapeItem *> list_shapes;
|
|
||||||
QVector<QetGraphicsTableItem *> table_vector;
|
|
||||||
QVector<TerminalStripItem *> strip_vector;
|
|
||||||
|
|
||||||
//Ckeck graphics item to "XMLise"
|
|
||||||
for(QGraphicsItem *qgi : items())
|
|
||||||
{
|
|
||||||
switch (qgi->type())
|
|
||||||
{
|
|
||||||
case Element::Type: {
|
|
||||||
auto elmt = static_cast<Element *>(qgi);
|
|
||||||
if (whole_content || elmt->isSelected()){
|
|
||||||
// For a copy/paste command, the text positions must be recalculated for
|
|
||||||
// correct text alignment, before it is saved to the clipboard
|
|
||||||
if(is_copy_command && (elmt->linkType() == Element::Slave || elmt->linkType()&Element::AllReport))
|
|
||||||
correctTextPos(elmt);
|
|
||||||
list_elements << elmt;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Conductor::Type: {
|
|
||||||
auto cond = static_cast<Conductor *>(qgi);
|
|
||||||
if (whole_content)
|
|
||||||
list_conductors << cond;
|
|
||||||
/* When we did not export the whole diagram,
|
|
||||||
* we must remove the non-selected conductors.
|
|
||||||
* At this step that means a conductor which one
|
|
||||||
* of these two element are not selected
|
|
||||||
*/
|
*/
|
||||||
else if (cond->terminal1->parentItem()->isSelected()
|
QString Diagram::headerXml()
|
||||||
&& cond->terminal2->parentItem()->isSelected())
|
|
||||||
list_conductors << cond;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DiagramImageItem::Type: {
|
|
||||||
auto image = static_cast<DiagramImageItem *>(qgi);
|
|
||||||
if (whole_content || image->isSelected())
|
|
||||||
list_images << image;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IndependentTextItem::Type: {
|
|
||||||
auto indi_text = static_cast<IndependentTextItem *>(qgi);
|
|
||||||
if (whole_content || indi_text->isSelected())
|
|
||||||
list_texts << indi_text;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case QetShapeItem::Type: {
|
|
||||||
auto shape = static_cast<QetShapeItem *>(qgi);
|
|
||||||
if (whole_content || shape->isSelected())
|
|
||||||
list_shapes << shape;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case QetGraphicsTableItem::Type: {
|
|
||||||
auto table = static_cast<QetGraphicsTableItem *>(qgi);
|
|
||||||
if (whole_content || table->isSelected())
|
|
||||||
table_vector << table;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TerminalStripItem::Type: {
|
|
||||||
const auto strip = static_cast<TerminalStripItem *>(qgi);
|
|
||||||
if (whole_content || strip->isSelected()) {
|
|
||||||
strip_vector << strip;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// items() returns items in stacking order, which is not guaranteed
|
|
||||||
// reproducible across processes (ties between same-Z items follow
|
|
||||||
// the scene's internal index, not any content-derived order) -- so
|
|
||||||
// without this, saving an unmodified project produces a different
|
|
||||||
// byte stream on every run. Elements and conductors are the two
|
|
||||||
// blocks observed to actually churn across the example corpus;
|
|
||||||
// sort them into a deterministic, content-derived order before
|
|
||||||
// serializing. This also fixes the legacy terminal-id churn below,
|
|
||||||
// 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); });
|
|
||||||
std::stable_sort(list_conductors.begin(), list_conductors.end(),
|
|
||||||
[](Conductor *a, Conductor *b) { return conductorSortKey(a) < conductorSortKey(b); });
|
|
||||||
|
|
||||||
// A copy carries the numberings its elements follow: pasted into
|
|
||||||
// another project, which does not know them, it can offer to import
|
|
||||||
// them (see PasteNumberingImport)
|
|
||||||
if (is_copy_command) {
|
|
||||||
ElementAutoNumSchemeCommand::writeCopiedSchemes(document, dom_root, m_project, list_elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
// correspondence table between the addresses of the terminals and their ids
|
|
||||||
// table de correspondance entre les adresses des bornes et leurs ids
|
|
||||||
QHash<Terminal *, int> table_adr_id;
|
|
||||||
|
|
||||||
if (!list_elements.isEmpty()) {
|
|
||||||
auto dom_elements = document.createElement(QStringLiteral("elements"));
|
|
||||||
for (auto elmt : list_elements) {
|
|
||||||
QDomElement dom_element = elmt->toXml(document, table_adr_id);
|
|
||||||
ItemGroups::write(dom_element, elmt);
|
|
||||||
dom_elements.appendChild(dom_element);
|
|
||||||
// If copy is active we have to undo the changes we have made during creating(filling) 'list_elements'
|
|
||||||
if(is_copy_command && (elmt->linkType() == Element::Slave || elmt->linkType()&Element::AllReport))
|
|
||||||
restoreText(elmt);
|
|
||||||
}
|
|
||||||
dom_root.appendChild(dom_elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!list_conductors.isEmpty()) {
|
|
||||||
//Symbols copied in old versions can share one uuid. A wire
|
|
||||||
//saved by that uuid would reopen on the first of them (#1408).
|
|
||||||
QSet<QUuid> seen_uuids, shared_uuids;
|
|
||||||
for (auto elmt : std::as_const(list_elements)) {
|
|
||||||
if (seen_uuids.contains(elmt->uuid()))
|
|
||||||
shared_uuids.insert(elmt->uuid());
|
|
||||||
seen_uuids.insert(elmt->uuid());
|
|
||||||
}
|
|
||||||
auto dom_conductors = document.createElement(QStringLiteral("conductors"));
|
|
||||||
for (auto cond : list_conductors) {
|
|
||||||
dom_conductors.appendChild(cond->toXml(document,
|
|
||||||
table_adr_id,
|
|
||||||
shared_uuids));
|
|
||||||
}
|
|
||||||
dom_root.appendChild(dom_conductors);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A rect query, unlike items(), returns true stacking order (z, then
|
|
||||||
// insertion order) even with NoIndex.
|
|
||||||
QHash<const QGraphicsItem *, int> stack_rank;
|
|
||||||
{
|
{
|
||||||
const QList<QGraphicsItem *> stacked = items(
|
QDomDocument document;
|
||||||
QRectF(-1e9, -1e9, 2e9, 2e9), Qt::IntersectsItemBoundingRect,
|
auto dom_root = document.createElement(QStringLiteral("diagram"));
|
||||||
Qt::AscendingOrder);
|
document.appendChild(dom_root);
|
||||||
for (int i = 0 ; i < stacked.size() ; ++i)
|
writeHeader(document, dom_root);
|
||||||
stack_rank.insert(stacked.at(i), i);
|
return document.toString();
|
||||||
}
|
|
||||||
appendInStackingOrder(document, dom_root, QStringLiteral("inputs"), list_texts, stack_rank);
|
|
||||||
appendInStackingOrder(document, dom_root, QStringLiteral("images"), list_images, stack_rank);
|
|
||||||
appendInStackingOrder(document, dom_root, QStringLiteral("shapes"), list_shapes, stack_rank);
|
|
||||||
appendInStackingOrder(document, dom_root, QStringLiteral("tables"), table_vector, stack_rank);
|
|
||||||
|
|
||||||
if (!strip_vector.isEmpty()) {
|
|
||||||
dom_root.appendChild(TerminalStripItemXml::toXml(strip_vector, document));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return(document);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1692,8 +1772,9 @@ bool Diagram::fromXml(QDomElement &document,
|
|||||||
return(false);
|
return(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read attributes of this diagram
|
// Read attributes of this diagram (already read for a folio whose
|
||||||
if (consider_informations)
|
// items are built later, see ensureLoaded())
|
||||||
|
if (consider_informations && !m_loading_deferred)
|
||||||
{
|
{
|
||||||
// Restore the persisted folio uuid. Done first, before any item is
|
// Restore the persisted folio uuid. Done first, before any item is
|
||||||
// loaded, so that everything created below sees the final uuid.
|
// 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
|
// persisted, or uuid already taken by another folio) gets a
|
||||||
// deterministic one instead, see derivedUuid().
|
// deterministic one instead, see derivedUuid().
|
||||||
const QUuid persisted_uuid(root.attribute(QStringLiteral("uuid")));
|
const QUuid persisted_uuid(root.attribute(QStringLiteral("uuid")));
|
||||||
|
const QUuid old_uuid = m_uuid;
|
||||||
if (persisted_uuid.isNull()) {
|
if (persisted_uuid.isNull()) {
|
||||||
m_uuid = derivedUuid(root, QStringLiteral("legacy"));
|
m_uuid = derivedUuid(root, QStringLiteral("legacy"));
|
||||||
} else if (uuidUsedByOtherDiagram(persisted_uuid)) {
|
} else if (uuidUsedByOtherDiagram(persisted_uuid)) {
|
||||||
@@ -1709,6 +1791,9 @@ bool Diagram::fromXml(QDomElement &document,
|
|||||||
} else {
|
} else {
|
||||||
m_uuid = persisted_uuid;
|
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
|
// Load border and titleblock
|
||||||
border_and_titleblock.titleBlockFromXml(root);
|
border_and_titleblock.titleBlockFromXml(root);
|
||||||
@@ -1776,6 +1861,16 @@ bool Diagram::fromXml(QDomElement &document,
|
|||||||
return(true);
|
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 paste from another project
|
||||||
if (root.hasAttribute(QStringLiteral("projectId")))
|
if (root.hasAttribute(QStringLiteral("projectId")))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -140,6 +140,11 @@ class Diagram : public QGraphicsScene
|
|||||||
bool m_freeze_new_elements;
|
bool m_freeze_new_elements;
|
||||||
bool m_freeze_new_conductors_;
|
bool m_freeze_new_conductors_;
|
||||||
QUuid m_uuid = QUuid::createUuid();
|
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()
|
//Selection before the current click, see completeGroupSelection()
|
||||||
QList<QPointer<QGraphicsObject>> m_previous_selection;
|
QList<QPointer<QGraphicsObject>> m_previous_selection;
|
||||||
@@ -200,10 +205,26 @@ class Diagram : public QGraphicsScene
|
|||||||
|
|
||||||
// methods related to XML import/export
|
// methods related to XML import/export
|
||||||
QDomDocument toXml(bool wholeContent = true, bool is_copy_command = false);
|
QDomDocument toXml(bool wholeContent = true, bool is_copy_command = false);
|
||||||
|
void writeHeader(QDomDocument &document, QDomElement &dom_root);
|
||||||
bool initFromXml(QDomElement &,
|
bool initFromXml(QDomElement &,
|
||||||
QPointF = QPointF(),
|
QPointF = QPointF(),
|
||||||
bool = true,
|
bool = true,
|
||||||
DiagramContent * = nullptr);
|
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
|
//The last argument is only for a copy fragment: when it is
|
||||||
//given, the cable lines that fragment carries are built as
|
//given, the cable lines that fragment carries are built as
|
||||||
//well and handed back through it -- they are not part of a
|
//well and handed back through it -- they are not part of a
|
||||||
|
|||||||
@@ -1645,6 +1645,25 @@ void DiagramView::editDiagramProperties()
|
|||||||
DiagramPropertiesDialog::diagramPropertiesDialog(m_diagram, diagramEditor());
|
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
|
@brief DiagramView::adjustSceneRect
|
||||||
Calcul and set the area of the scene visualized by this view
|
Calcul and set the area of the scene visualized by this view
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ class DiagramView : public PaletteGraphicsView
|
|||||||
void paste(const QPointF & = QPointF(), QClipboard::Mode = QClipboard::Clipboard);
|
void paste(const QPointF & = QPointF(), QClipboard::Mode = QClipboard::Clipboard);
|
||||||
void pasteHere();
|
void pasteHere();
|
||||||
void duplicate(const QPoint &stepOffset);
|
void duplicate(const QPoint &stepOffset);
|
||||||
|
void buildFolio();
|
||||||
void adjustSceneRect();
|
void adjustSceneRect();
|
||||||
void updateWindowTitle();
|
void updateWindowTitle();
|
||||||
void resetConductors();
|
void resetConductors();
|
||||||
|
|||||||
+58
-11
@@ -30,11 +30,10 @@
|
|||||||
@param prj the project where we must find element
|
@param prj the project where we must find element
|
||||||
@param diagram the diagram to exclude from the search
|
@param diagram the diagram to exclude from the search
|
||||||
*/
|
*/
|
||||||
ElementProvider::ElementProvider(QETProject *prj, Diagram *diagram)
|
ElementProvider::ElementProvider(QETProject *prj, Diagram *diagram) :
|
||||||
{
|
m_project(prj),
|
||||||
m_diagram_list = prj->diagrams();
|
m_excluded(diagram)
|
||||||
m_diagram_list.removeOne(diagram);
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief ElementProvider::ElementProvider Constructor
|
@brief ElementProvider::ElementProvider Constructor
|
||||||
@@ -44,6 +43,46 @@ ElementProvider::ElementProvider(Diagram *diag) {
|
|||||||
m_diagram_list << 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
|
@brief ElementProvider::FreeElement
|
||||||
Search and return the asked element corresponding with the given filter
|
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;
|
QVector<QPointer<Element>> free_elmt;
|
||||||
QList<Element *> elmt_list;
|
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
|
//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
|
//get all element in diagram d
|
||||||
elmt_list = diagram_->elements();
|
elmt_list = diagram_->elements();
|
||||||
for (const auto &elmt_ : std::as_const(elmt_list))
|
for (const auto &elmt_ : std::as_const(elmt_list))
|
||||||
{
|
{
|
||||||
if (filter & elmt_->elementData().m_type &&
|
if (filter & elmt_->elementData().m_type &&
|
||||||
elmt_->isFree())
|
elmt_->isFree() &&
|
||||||
|
!elmt_->pendingLinks().intersects(waiting))
|
||||||
{
|
{
|
||||||
free_elmt << elmt_;
|
free_elmt << elmt_;
|
||||||
}
|
}
|
||||||
@@ -86,7 +131,7 @@ QList <Element *> ElementProvider::fromUuids(QList<QUuid> uuid_list) const
|
|||||||
{
|
{
|
||||||
QList <Element *> found_element;
|
QList <Element *> found_element;
|
||||||
|
|
||||||
foreach (Diagram *d, m_diagram_list) {
|
foreach (Diagram *d, allFolios()) {
|
||||||
foreach(Element *elmt, d->elements()) {
|
foreach(Element *elmt, d->elements()) {
|
||||||
if (uuid_list.contains(elmt->uuid())) {
|
if (uuid_list.contains(elmt->uuid())) {
|
||||||
found_element << elmt;
|
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>> ElementProvider::find(ElementData::Types elmt_type) const
|
||||||
{
|
{
|
||||||
QVector<QPointer<Element>> returned_vector;
|
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();
|
const auto elmt_list = diagram_->elements();
|
||||||
for (const auto &elmt_ : elmt_list)
|
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())
|
for (auto item_ : d->items())
|
||||||
{
|
{
|
||||||
if(item_->type() == QetGraphicsTableItem::Type)
|
if(item_->type() == QetGraphicsTableItem::Type)
|
||||||
@@ -197,8 +243,9 @@ QetGraphicsTableItem *ElementProvider::tableFromUuid(const QUuid &uuid)
|
|||||||
QVector<TerminalElement *> ElementProvider::freeTerminal() const
|
QVector<TerminalElement *> ElementProvider::freeTerminal() const
|
||||||
{
|
{
|
||||||
QVector<TerminalElement *> vector_;
|
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()};
|
const auto elmt_list{diagram->elements()};
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class ElementProvider
|
|||||||
public:
|
public:
|
||||||
ElementProvider(QETProject *prj, Diagram *diagram=nullptr);
|
ElementProvider(QETProject *prj, Diagram *diagram=nullptr);
|
||||||
ElementProvider(Diagram *diag);
|
ElementProvider(Diagram *diag);
|
||||||
|
explicit ElementProvider(const QList<Diagram *> &diagrams);
|
||||||
QVector <QPointer<Element>> freeElement(ElementData::Types filter) const;
|
QVector <QPointer<Element>> freeElement(ElementData::Types filter) const;
|
||||||
QList <Element *> fromUuids(QList <QUuid>) const;
|
QList <Element *> fromUuids(QList <QUuid>) const;
|
||||||
QVector<QPointer<Element> > find(ElementData::Types elmt_type) const;
|
QVector<QPointer<Element> > find(ElementData::Types elmt_type) const;
|
||||||
@@ -49,7 +50,15 @@ class ElementProvider
|
|||||||
QVector<TerminalElement *> freeTerminal() const;
|
QVector<TerminalElement *> freeTerminal() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QList<Diagram *> folios(ElementData::Types kinds, bool free_only) const;
|
||||||
|
QList<Diagram *> allFolios() const;
|
||||||
|
|
||||||
QList <Diagram *> m_diagram_list;
|
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
|
#endif // ELEMENTPROVIDER_H
|
||||||
|
|||||||
@@ -473,14 +473,16 @@ void ElementsPanelWidget::updateButtons()
|
|||||||
if (!selected_diagrams.isEmpty()) {
|
if (!selected_diagrams.isEmpty()) {
|
||||||
QETProject *selected_diagram_project = selected_diagrams.first() -> project();
|
QETProject *selected_diagram_project = selected_diagrams.first() -> project();
|
||||||
bool is_writable = !(selected_diagram_project -> isReadOnly());
|
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
|
// Find the highest (min) and lowest (max) index among the selection
|
||||||
int min_position = project_diagrams_count;
|
int min_position = project_diagrams_count;
|
||||||
int max_position = -1;
|
int max_position = -1;
|
||||||
|
|
||||||
for (Diagram *diagram : selected_diagrams) {
|
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 < min_position) min_position = pos;
|
||||||
if (pos > max_position) max_position = pos;
|
if (pos > max_position) max_position = pos;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,11 +253,13 @@ QTreeWidgetItem *GenericPanel::fillProjectItem(QTreeWidgetItem *project_qtwi,
|
|||||||
this,&GenericPanel::projectDiagramsOrderChanged);
|
this,&GenericPanel::projectDiagramsOrderChanged);
|
||||||
} else {
|
} else {
|
||||||
// remove diagrams unknown to the project (presumably removed)
|
// remove diagrams unknown to the project (presumably removed)
|
||||||
removeObsoleteItems(project -> diagrams(),
|
removeObsoleteItems(project -> folios(),
|
||||||
project_qtwi, QET::Diagram, false);
|
project_qtwi, QET::Diagram, false);
|
||||||
}
|
}
|
||||||
int index = 0;
|
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,
|
QTreeWidgetItem *diagram_qtwi = addDiagram(diagram,
|
||||||
nullptr,
|
nullptr,
|
||||||
options);
|
options);
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ void ProjectPrintWindow::requestPaint()
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!m_project->diagrams().count()) {
|
if (!m_project->folios().count()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-2
@@ -959,7 +959,9 @@ void ProjectView::loadDiagrams()
|
|||||||
{
|
{
|
||||||
if (!m_project) return;
|
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();
|
auto dialog = DialogWaiting::instance();
|
||||||
if(dialog)
|
if(dialog)
|
||||||
@@ -968,7 +970,7 @@ void ProjectView::loadDiagrams()
|
|||||||
"...</b><br/>Creating sheet tabs :</p>"));
|
"...</b><br/>Creating sheet tabs :</p>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto diagram : m_project->diagrams())
|
for(auto diagram : m_project->folios())
|
||||||
{
|
{
|
||||||
if(dialog)
|
if(dialog)
|
||||||
{
|
{
|
||||||
@@ -1202,6 +1204,12 @@ void ProjectView::rebuildDiagramsMap()
|
|||||||
*/
|
*/
|
||||||
void ProjectView::tabChanged(int tab_id)
|
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)
|
if (tab_id == -1)
|
||||||
setDisplayFallbackWidget(true);
|
setDisplayFallbackWidget(true);
|
||||||
else if(m_tab->count() == 1)
|
else if(m_tab->count() == 1)
|
||||||
|
|||||||
+20
-2
@@ -770,6 +770,15 @@ bool QET::compareCanonicalFilePaths(const QString &first, const QString &second)
|
|||||||
@return false if an error occurred, true otherwise
|
@return false if an error occurred, true otherwise
|
||||||
*/
|
*/
|
||||||
bool QET::writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString *error_message)
|
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);
|
QSaveFile file(filepath);
|
||||||
|
|
||||||
@@ -789,7 +798,7 @@ bool QET::writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString *
|
|||||||
QTextStream out(&file);
|
QTextStream out(&file);
|
||||||
out.setEncoding(QStringConverter::Utf8);
|
out.setEncoding(QStringConverter::Utf8);
|
||||||
out.setGenerateByteOrderMark(false);
|
out.setGenerateByteOrderMark(false);
|
||||||
out << xml_doc.toString(4);
|
out << text;
|
||||||
if (!file.commit())
|
if (!file.commit())
|
||||||
{
|
{
|
||||||
if (error_message) {
|
if (error_message) {
|
||||||
@@ -895,6 +904,15 @@ QActionGroup *QET::depthActionGroup(QObject *parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool QET::writeToFile(QDomDocument &xml_doc, QFile *file, QString *error_message)
|
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;
|
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.seek(0);
|
||||||
out.setEncoding(QStringConverter::Utf8);
|
out.setEncoding(QStringConverter::Utf8);
|
||||||
out.setGenerateByteOrderMark(false);
|
out.setGenerateByteOrderMark(false);
|
||||||
out << xml_doc.toString(4);
|
out << text;
|
||||||
if (opened_here) {
|
if (opened_here) {
|
||||||
file->close();
|
file->close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,6 +202,8 @@ namespace QET {
|
|||||||
bool compareCanonicalFilePaths(const QString &, const QString &);
|
bool compareCanonicalFilePaths(const QString &, const QString &);
|
||||||
bool writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString * error_message= nullptr);
|
bool writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString * error_message= nullptr);
|
||||||
bool writeToFile (QDomDocument &xml_doc, QFile *file, 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);
|
bool eachStrIsEqual (const QStringList &qsl);
|
||||||
QActionGroup *depthActionGroup(QObject *parent = nullptr);
|
QActionGroup *depthActionGroup(QObject *parent = nullptr);
|
||||||
void saveCustomColors();
|
void saveCustomColors();
|
||||||
|
|||||||
@@ -562,8 +562,9 @@ void QETDiagramEditor::setUpActions()
|
|||||||
m_draw_grid->setCheckable(true);
|
m_draw_grid->setCheckable(true);
|
||||||
m_draw_grid->setChecked(settings.value("diagrameditor/grid_display_startup", true).toBool());
|
m_draw_grid->setChecked(settings.value("diagrameditor/grid_display_startup", true).toBool());
|
||||||
connect(m_draw_grid, &QAction::triggered, [this](bool checked) {
|
connect(m_draw_grid, &QAction::triggered, [this](bool checked) {
|
||||||
|
//The folio's own setting: folios() builds nothing
|
||||||
foreach (ProjectView *prjv, this->openedProjects())
|
foreach (ProjectView *prjv, this->openedProjects())
|
||||||
foreach (Diagram *d, prjv->project()->diagrams()) {
|
foreach (Diagram *d, prjv->project()->folios()) {
|
||||||
d->setDisplayGrid(checked);
|
d->setDisplayGrid(checked);
|
||||||
d->update();
|
d->update();
|
||||||
}
|
}
|
||||||
@@ -603,8 +604,9 @@ void QETDiagramEditor::setUpActions()
|
|||||||
m_draw_guides->setCheckable(true);
|
m_draw_guides->setCheckable(true);
|
||||||
m_draw_guides->setChecked(settings.value("diagrameditor/guides_display_startup", false).toBool());
|
m_draw_guides->setChecked(settings.value("diagrameditor/guides_display_startup", false).toBool());
|
||||||
connect(m_draw_guides, &QAction::triggered, [this](bool checked) {
|
connect(m_draw_guides, &QAction::triggered, [this](bool checked) {
|
||||||
|
//The folio's own setting: folios() builds nothing
|
||||||
foreach (ProjectView *prjv, this->openedProjects())
|
foreach (ProjectView *prjv, this->openedProjects())
|
||||||
foreach (Diagram *d, prjv->project()->diagrams()) {
|
foreach (Diagram *d, prjv->project()->folios()) {
|
||||||
d->setDisplayGuides(checked);
|
d->setDisplayGuides(checked);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -652,8 +654,10 @@ void QETDiagramEditor::setUpActions()
|
|||||||
action->setData(int(kind.first));
|
action->setData(int(kind.first));
|
||||||
connect(action, &QAction::triggered, this, [kind](bool checked) {
|
connect(action, &QAction::triggered, this, [kind](bool checked) {
|
||||||
ShownKinds::setShown(kind.first, 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 (QETProject *project : QETApp::registeredProjects())
|
||||||
for (Diagram *diagram : project->diagrams())
|
for (Diagram *diagram : project->builtFolios())
|
||||||
ShownKinds::apply(diagram, kind.first);
|
ShownKinds::apply(diagram, kind.first);
|
||||||
emit QETApp::instance()->shownKindsChanged();
|
emit QETApp::instance()->shownKindsChanged();
|
||||||
});
|
});
|
||||||
@@ -2004,7 +2008,7 @@ bool QETDiagramEditor::openAndAddProject(
|
|||||||
//was not found: they would otherwise vanish on the next save
|
//was not found: they would otherwise vanish on the next save
|
||||||
//without the user ever being told.
|
//without the user ever being told.
|
||||||
QStringList lost_wires;
|
QStringList lost_wires;
|
||||||
for (Diagram *diagram : project->diagrams()) {
|
for (Diagram *diagram : project->foliosWithWiresNotReconnected()) {
|
||||||
for (const QString &wire : diagram->wiresNotReconnected()) {
|
for (const QString &wire : diagram->wiresNotReconnected()) {
|
||||||
lost_wires << tr("Sheet %1: %2").arg(diagram->folioIndex() + 1).arg(wire);
|
lost_wires << tr("Sheet %1: %2").arg(diagram->folioIndex() + 1).arg(wire);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,8 @@
|
|||||||
*/
|
*/
|
||||||
ProjectDBModel::ProjectDBModel(QETProject *project, QObject *parent) :
|
ProjectDBModel::ProjectDBModel(QETProject *project, QObject *parent) :
|
||||||
QAbstractTableModel(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);
|
connect(m_project->dataBase(), &projectDataBase::dataBaseUpdated, this, &ProjectDBModel::dataBaseUpdated);
|
||||||
}
|
}
|
||||||
@@ -320,6 +321,21 @@ void ProjectDBModel::setIdentifier(const QString &identifier) {
|
|||||||
m_identifier = 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
|
@brief ProjectDBModel::dataBaseUpdated
|
||||||
slot called when the project database is updated
|
slot called when the project database is updated
|
||||||
@@ -374,6 +390,13 @@ void ProjectDBModel::setHeaderString()
|
|||||||
void ProjectDBModel::fillValue()
|
void ProjectDBModel::fillValue()
|
||||||
{
|
{
|
||||||
m_record.clear();
|
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);
|
auto query_ = m_project->dataBase()->newQuery(m_query);
|
||||||
if (!query_.exec()) {
|
if (!query_.exec()) {
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class ProjectDBModel : public QAbstractTableModel
|
|||||||
void setIdentifier(const QString &identifier);
|
void setIdentifier(const QString &identifier);
|
||||||
QString identifier() const {return m_identifier;}
|
QString identifier() const {return m_identifier;}
|
||||||
static QString xmlTagName() {return QString("project_data_base_model");}
|
static QString xmlTagName() {return QString("project_data_base_model");}
|
||||||
|
void folioBuilt();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void dataBaseUpdated();
|
void dataBaseUpdated();
|
||||||
@@ -79,6 +80,7 @@ class ProjectDBModel : public QAbstractTableModel
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<QETProject> m_project;
|
QPointer<QETProject> m_project;
|
||||||
|
bool m_fill_when_built = false;
|
||||||
QString m_query;
|
QString m_query;
|
||||||
QVector<QStringList> m_record;
|
QVector<QStringList> m_record;
|
||||||
//First int = section, second int = Qt::role, QVariant = value
|
//First int = section, second int = Qt::role, QVariant = value
|
||||||
|
|||||||
@@ -565,15 +565,34 @@ void QetGraphicsTableItem::initLink()
|
|||||||
{
|
{
|
||||||
if (!m_pending_previous_table_uuid.isNull())
|
if (!m_pending_previous_table_uuid.isNull())
|
||||||
{
|
{
|
||||||
ElementProvider provider_(this->diagram()->project());
|
//The built folios only: a previous table on a folio not built
|
||||||
if (auto previous_table = provider_.tableFromUuid(m_pending_previous_table_uuid)) {
|
//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);
|
setPreviousTable(previous_table);
|
||||||
}
|
}
|
||||||
|
if (previous_table || !project->unloadedFolioCount()) {
|
||||||
m_pending_previous_table_uuid = QUuid(); //Set to null in case initLink is called again
|
m_pending_previous_table_uuid = QUuid(); //Set to null in case initLink is called again
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setSize(m_pending_size);
|
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
|
@brief QetGraphicsTableItem::minimumRowHeight
|
||||||
@return the minimum height of a row
|
@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;
|
const auto project_ = diagram() ? diagram()->project() : nullptr;
|
||||||
auto in_project = [project_](const QetGraphicsTableItem *table) {
|
auto in_project = [project_](const QetGraphicsTableItem *table) {
|
||||||
return project_ && table->diagram()
|
return project_ && table->diagram()
|
||||||
&& project_->diagrams().contains(table->diagram());
|
&& project_->folios().contains(table->diagram());
|
||||||
};
|
};
|
||||||
QetGraphicsTableItem *previous_ = m_previous_table;
|
QetGraphicsTableItem *previous_ = m_previous_table;
|
||||||
while (previous_ && !in_project(previous_)) {
|
while (previous_ && !in_project(previous_)) {
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ class QetGraphicsTableItem : public QetGraphicsItem
|
|||||||
QPointer<QetGraphicsTableItem> nextTable() const;
|
QPointer<QetGraphicsTableItem> nextTable() const;
|
||||||
void setToMinimumHeight();
|
void setToMinimumHeight();
|
||||||
void initLink();
|
void initLink();
|
||||||
|
bool hasPendingLink() const {return !m_pending_previous_table_uuid.isNull();}
|
||||||
|
QUuid previousTableUuid() const;
|
||||||
QUuid uuid() const {return m_uuid;}
|
QUuid uuid() const {return m_uuid;}
|
||||||
int minimumRowHeight() const;
|
int minimumRowHeight() const;
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "../conductorsegment.h"
|
#include "../conductorsegment.h"
|
||||||
#include "../conductorsegmentprofile.h"
|
#include "../conductorsegmentprofile.h"
|
||||||
#include "../diagram.h"
|
#include "../diagram.h"
|
||||||
|
#include "../dataBase/projectdatabase.h"
|
||||||
#include "../diagramcommands.h"
|
#include "../diagramcommands.h"
|
||||||
#include "../qetdiagrameditor.h"
|
#include "../qetdiagrameditor.h"
|
||||||
#include "../qetgraphicsitem/terminal.h"
|
#include "../qetgraphicsitem/terminal.h"
|
||||||
@@ -118,6 +119,8 @@ Conductor::Conductor(Terminal *p1, Terminal* p2) :
|
|||||||
setZValue(11);
|
setZValue(11);
|
||||||
m_previous_z_value = zValue();
|
m_previous_z_value = zValue();
|
||||||
m_uuid = QUuid::createUuid();
|
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
|
//Add this conductor to the list of conductors of each of the two terminals
|
||||||
bool ajout_p1 = terminal1 -> addConductor(this);
|
bool ajout_p1 = terminal1 -> addConductor(this);
|
||||||
@@ -1114,6 +1117,7 @@ void Conductor::pointsToSegments(const QList<QPointF>& points_list) {
|
|||||||
*/
|
*/
|
||||||
bool Conductor::fromXml(QDomElement &dom_element)
|
bool Conductor::fromXml(QDomElement &dom_element)
|
||||||
{
|
{
|
||||||
|
const QUuid old_uuid = m_uuid;
|
||||||
//Older project files have no conductor uuid attribute at all --
|
//Older project files have no conductor uuid attribute at all --
|
||||||
//generate one on load, same treatment terminal uuids got when
|
//generate one on load, same treatment terminal uuids got when
|
||||||
//that field was introduced (see terminal1/terminal2 handling in
|
//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).
|
//single load-and-resave (see #754).
|
||||||
m_uuid = QUuid::createUuid();
|
m_uuid = QUuid::createUuid();
|
||||||
}
|
}
|
||||||
|
uuidChanged(old_uuid);
|
||||||
|
|
||||||
//"nan" and "inf" parse as numbers; a non-finite position is kept at 0
|
//"nan" and "inf" parse as numbers; a non-finite position is kept at 0
|
||||||
const qreal x = dom_element.attribute("x", nullptr).toDouble();
|
const qreal x = dom_element.attribute("x", nullptr).toDouble();
|
||||||
@@ -1235,7 +1240,7 @@ QDomElement Conductor::toXml(QDomDocument &dom_document,
|
|||||||
dom_element.appendChild(dom_seq);
|
dom_element.appendChild(dom_seq);
|
||||||
|
|
||||||
// Export the properties and text
|
// Export the properties and text
|
||||||
m_properties. toXml(dom_element);
|
properties().toXml(dom_element);
|
||||||
if(m_text_item->wasMovedByUser())
|
if(m_text_item->wasMovedByUser())
|
||||||
{
|
{
|
||||||
dom_element.setAttribute("userx", QString::number(m_text_item->pos().x()));
|
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_properties.text = m_properties.m_formula;
|
||||||
m_text_item->setPlainText(m_properties.text);
|
m_text_item->setPlainText(m_properties.text);
|
||||||
}
|
}
|
||||||
|
storeProperties();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1833,6 +1839,7 @@ void Conductor::setProperties(const ConductorProperties &property)
|
|||||||
calculateTextItemPosition();
|
calculateTextItemPosition();
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
storeProperties();
|
||||||
emit propertiesChange();
|
emit propertiesChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1842,9 +1849,75 @@ void Conductor::setProperties(const ConductorProperties &property)
|
|||||||
*/
|
*/
|
||||||
ConductorProperties Conductor::properties() const
|
ConductorProperties Conductor::properties() const
|
||||||
{
|
{
|
||||||
|
if (projectDataBase *store = placedStore())
|
||||||
|
return store->conductorProperties(m_uuid);
|
||||||
return(m_properties);
|
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
|
@brief Conductor::setCableReference
|
||||||
Record the cable core this conductor shows, together with the label
|
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 = label;
|
||||||
m_properties.m_cable_uuid = cable_uuid;
|
m_properties.m_cable_uuid = cable_uuid;
|
||||||
m_properties.m_cable_slot = slot;
|
m_properties.m_cable_slot = slot;
|
||||||
|
storeProperties();
|
||||||
emit propertiesChange();
|
emit propertiesChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
class ConductorProfile;
|
class ConductorProfile;
|
||||||
class ConductorSegmentProfile;
|
class ConductorSegmentProfile;
|
||||||
class Diagram;
|
class Diagram;
|
||||||
|
class projectDataBase;
|
||||||
class Terminal;
|
class Terminal;
|
||||||
class ConductorSegment;
|
class ConductorSegment;
|
||||||
class ConductorTextItem;
|
class ConductorTextItem;
|
||||||
@@ -80,8 +81,8 @@ class Conductor : public QGraphicsObject
|
|||||||
Diagram *diagram() const;
|
Diagram *diagram() const;
|
||||||
ConductorTextItem *textItem() const;
|
ConductorTextItem *textItem() const;
|
||||||
QUuid uuid() const {return m_uuid;}
|
QUuid uuid() const {return m_uuid;}
|
||||||
void newUuid() {m_uuid = QUuid::createUuid(); m_persist_uuid = true;} //create new uuid for this conductor
|
void newUuid(); //create new uuid for this conductor
|
||||||
void setUuid(const QUuid &uuid) {m_uuid = uuid; m_persist_uuid = true;} //saved from now on
|
void setUuid(const QUuid &uuid); //saved from now on
|
||||||
void updatePath(const QRectF & = QRectF());
|
void updatePath(const QRectF & = QRectF());
|
||||||
|
|
||||||
//This method do nothing, it's only made to be used with Q_PROPERTY
|
//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);
|
bool only_text = false);
|
||||||
void setProperties(const ConductorProperties &property);
|
void setProperties(const ConductorProperties &property);
|
||||||
ConductorProperties properties() const;
|
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
|
/// Records which cable core this conductor shows and writes the
|
||||||
/// label that assignment generates (15W1:br) into its cable
|
/// label that assignment generates (15W1:br) into its cable
|
||||||
/// field. Deliberately not routed through
|
/// field. Deliberately not routed through
|
||||||
@@ -233,6 +239,10 @@ class Conductor : public QGraphicsObject
|
|||||||
/// file had none -- toXml() must not persist that value, or
|
/// file had none -- toXml() must not persist that value, or
|
||||||
/// every reload mints and saves a new random one (see #754).
|
/// every reload mints and saves a new random one (see #754).
|
||||||
bool m_persist_uuid = true;
|
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
|
/// QPen et QBrush objects used to draw conductors
|
||||||
static QPen conductor_pen;
|
static QPen conductor_pen;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "element.h"
|
#include "element.h"
|
||||||
#include "../qetapp.h"
|
#include "../qetapp.h"
|
||||||
#include "../qetproject.h"
|
#include "../qetproject.h"
|
||||||
|
#include "../dataBase/projectdatabase.h"
|
||||||
#include "../PropertiesEditor/propertieseditordialog.h"
|
#include "../PropertiesEditor/propertieseditordialog.h"
|
||||||
#include "../autoNum/assignvariables.h"
|
#include "../autoNum/assignvariables.h"
|
||||||
#include "../autoNum/elementautonumschemecommand.h"
|
#include "../autoNum/elementautonumschemecommand.h"
|
||||||
@@ -718,6 +719,7 @@ bool Element::buildFromXml(const QDomElement &xml_def_elmt, int *state)
|
|||||||
m_data.m_informations.fromXml(
|
m_data.m_informations.fromXml(
|
||||||
xml_def_elmt.firstChildElement(QStringLiteral("elementInformations")),
|
xml_def_elmt.firstChildElement(QStringLiteral("elementInformations")),
|
||||||
QStringLiteral("elementInformation"));
|
QStringLiteral("elementInformation"));
|
||||||
|
storeInformation();
|
||||||
|
|
||||||
//scroll of the Children of the Definition: Parts of the Drawing
|
//scroll of the Children of the Definition: Parts of the Drawing
|
||||||
int parsed_elements_count = 0;
|
int parsed_elements_count = 0;
|
||||||
@@ -1042,7 +1044,9 @@ bool Element::fromXml(QDomElement &e,
|
|||||||
}
|
}
|
||||||
|
|
||||||
//uuid of this element
|
//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
|
//load prefix
|
||||||
m_prefix = e.attribute(QStringLiteral("prefix"));
|
m_prefix = e.attribute(QStringLiteral("prefix"));
|
||||||
@@ -1265,10 +1269,11 @@ QDomElement Element::toXml(
|
|||||||
}
|
}
|
||||||
|
|
||||||
//save information of this element
|
//save information of this element
|
||||||
if (! m_data.m_informations.keys().isEmpty()) {
|
const DiagramContext informations = elementInformations();
|
||||||
|
if (! informations.keys().isEmpty()) {
|
||||||
QDomElement infos =
|
QDomElement infos =
|
||||||
document.createElement(QStringLiteral("elementInformations"));
|
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
|
//toXml() skips empty values: an element whose information is
|
||||||
//all empty would otherwise be written an empty block, which the
|
//all empty would otherwise be written an empty block, which the
|
||||||
//next load reads as no information and the next save drops.
|
//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 nothing to link return now
|
||||||
if (tmp_uuids_link.isEmpty()) return;
|
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;
|
QList<QUuid> uuids;
|
||||||
for (const auto &linkInfo : tmp_uuids_link) {
|
for (const auto &linkInfo : tmp_uuids_link) {
|
||||||
uuids.append(linkInfo.uuid);
|
uuids.append(linkInfo.uuid);
|
||||||
@@ -1643,8 +1651,28 @@ void Element::initLink(QETProject *prj)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
restoreSavedLinkOrder();
|
restoreSavedLinkOrder();
|
||||||
|
if (pendingLinks().isEmpty() || !prj->unloadedFolioCount()) {
|
||||||
tmp_uuids_link.clear();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Element::initLink
|
@brief Element::initLink
|
||||||
@@ -1674,6 +1702,23 @@ void Element::initLink(const QList<Element *> &candidates)
|
|||||||
tmp_uuids_link.clear();
|
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
|
@brief Element::restoreSavedLinkOrder
|
||||||
Put connected_elements back in the order tmp_uuids_link saved them.
|
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,
|
m_data.m_informations.addValue(QETInformation::ELMT_FORMULA_ID,
|
||||||
id.toString(), false);
|
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)
|
void Element::setElementInformations(DiagramContext dc)
|
||||||
@@ -1811,6 +1883,7 @@ void Element::setElementInformations(DiagramContext dc)
|
|||||||
if (!actual_label.isEmpty()) {
|
if (!actual_label.isEmpty()) {
|
||||||
m_data.m_informations.addValue(QStringLiteral("label"), actual_label); //Update the label if there is a formula
|
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);
|
emit elementInfoChange(old_info, m_data.m_informations);
|
||||||
|
|
||||||
// Propagate label change to linked PLC slaves (label is changed via
|
// Propagate label change to linked PLC slaves (label is changed via
|
||||||
@@ -1841,9 +1914,43 @@ void Element::setElementInformations(DiagramContext dc)
|
|||||||
*/
|
*/
|
||||||
ElementData Element::elementData() const
|
ElementData Element::elementData() const
|
||||||
{
|
{
|
||||||
|
if (projectDataBase *store = placedStore()) {
|
||||||
|
ElementData data = m_data;
|
||||||
|
data.m_informations = store->elementInformation(m_uuid);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
return m_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
|
* @brief Element::setElementData
|
||||||
* Set new data for this element.
|
* Set new data for this element.
|
||||||
@@ -1860,6 +1967,7 @@ void Element::setElementData(ElementData data)
|
|||||||
|
|
||||||
if (old_info != m_data.m_informations) {
|
if (old_info != m_data.m_informations) {
|
||||||
m_data.m_informations.addValue(QStringLiteral("label"), actualLabel()); //Update the label if there is a formula
|
m_data.m_informations.addValue(QStringLiteral("label"), actualLabel()); //Update the label if there is a formula
|
||||||
|
storeInformation();
|
||||||
if (diagram()) {
|
if (diagram()) {
|
||||||
diagram()->project()->dataBase()->elementInfoChanged(this);
|
diagram()->project()->dataBase()->elementInfoChanged(this);
|
||||||
}
|
}
|
||||||
@@ -2078,6 +2186,7 @@ void Element::setUpFormula(bool code_letter, QUndoCommand *parent_undo)
|
|||||||
->elementAutoNumCurrentFormula();
|
->elementAutoNumCurrentFormula();
|
||||||
|
|
||||||
m_data.m_informations.addValue(QStringLiteral("formula"), formula);
|
m_data.m_informations.addValue(QStringLiteral("formula"), formula);
|
||||||
|
storeInformation();
|
||||||
|
|
||||||
QString element_currentAutoNum = diagram()
|
QString element_currentAutoNum = diagram()
|
||||||
->project()
|
->project()
|
||||||
@@ -2146,6 +2255,7 @@ void Element::setUpFormula(bool code_letter, QUndoCommand *parent_undo)
|
|||||||
{
|
{
|
||||||
DiagramContext dc = m_data.m_informations;
|
DiagramContext dc = m_data.m_informations;
|
||||||
m_data.m_informations.addValue(QStringLiteral("label"), actualLabel());
|
m_data.m_informations.addValue(QStringLiteral("label"), actualLabel());
|
||||||
|
storeInformation();
|
||||||
emit elementInfoChange(dc, m_data.m_informations);
|
emit elementInfoChange(dc, m_data.m_informations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2199,6 +2309,8 @@ void Element::freezeNewAddedElement()
|
|||||||
*/
|
*/
|
||||||
QString Element::actualLabel()
|
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()) {
|
if (m_data.m_informations.value(QStringLiteral("formula")).toString().isEmpty()) {
|
||||||
return m_data.m_informations.value(QStringLiteral("label")).toString();
|
return m_data.m_informations.value(QStringLiteral("label")).toString();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ class QUndoCommand;
|
|||||||
/**
|
/**
|
||||||
This is the base class for electrical elements.
|
This is the base class for electrical elements.
|
||||||
*/
|
*/
|
||||||
|
class projectDataBase;
|
||||||
|
|
||||||
class Element : public QetGraphicsItem
|
class Element : public QetGraphicsItem
|
||||||
{
|
{
|
||||||
friend class DiagramEventAddElement;
|
friend class DiagramEventAddElement;
|
||||||
@@ -130,8 +132,9 @@ class Element : public QetGraphicsItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
//METHODS related to information
|
//METHODS related to information
|
||||||
DiagramContext elementInformations()const
|
DiagramContext elementInformations()const;
|
||||||
{return m_data.m_informations;}
|
/// 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);
|
virtual void setElementInformations(DiagramContext dc);
|
||||||
|
|
||||||
ElementData elementData() const;
|
ElementData elementData() const;
|
||||||
@@ -222,6 +225,9 @@ class Element : public QetGraphicsItem
|
|||||||
virtual void unlinkAllElements() {}
|
virtual void unlinkAllElements() {}
|
||||||
virtual void unlinkElement(Element *) {}
|
virtual void unlinkElement(Element *) {}
|
||||||
virtual void initLink(QETProject *);
|
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
|
Resolve tmp_uuids_link against a caller-supplied candidate
|
||||||
list instead of a project-wide search (bugtracker #607).
|
list instead of a project-wide search (bugtracker #607).
|
||||||
@@ -259,8 +265,8 @@ class Element : public QetGraphicsItem
|
|||||||
*/
|
*/
|
||||||
QString linkTypeToString() const;
|
QString linkTypeToString() const;
|
||||||
|
|
||||||
void newUuid() {m_uuid = QUuid::createUuid();} //create new uuid for this element
|
void newUuid() {setUuid(QUuid::createUuid());} //create new uuid for this element
|
||||||
void setUuid(const QUuid &uuid) {m_uuid = uuid;}
|
void setUuid(const QUuid &uuid);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void drawAxes(QPainter *, const QStyleOptionGraphicsItem *);
|
void drawAxes(QPainter *, const QStyleOptionGraphicsItem *);
|
||||||
@@ -329,6 +335,8 @@ class Element : public QetGraphicsItem
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void restoreSavedLinkOrder();
|
void restoreSavedLinkOrder();
|
||||||
|
void storeInformation();
|
||||||
|
projectDataBase *placedStore() const;
|
||||||
bool m_must_highlight = false;
|
bool m_must_highlight = false;
|
||||||
QSize dimensions;
|
QSize dimensions;
|
||||||
QPoint hotspot_coord;
|
QPoint hotspot_coord;
|
||||||
|
|||||||
@@ -1090,6 +1090,17 @@ QList<Terminal *> relatedPotentialTerminal (
|
|||||||
// If terminal parent element is a folio report.
|
// If terminal parent element is a folio report.
|
||||||
if (all_diagram && terminal -> parentElement() -> linkType() & Element::AllReport)
|
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();
|
QList <Element *> elmt_list = terminal -> parentElement() -> linkedElements();
|
||||||
if (!elmt_list.isEmpty())
|
if (!elmt_list.isEmpty())
|
||||||
{
|
{
|
||||||
|
|||||||
+784
-30
File diff suppressed because it is too large
Load Diff
+51
-1
@@ -31,6 +31,7 @@
|
|||||||
#include "wirehops.h"
|
#include "wirehops.h"
|
||||||
#include "wiringrules.h"
|
#include "wiringrules.h"
|
||||||
#include "diagram.h"
|
#include "diagram.h"
|
||||||
|
#include "autoNum/assignvariables.h"
|
||||||
#ifdef BUILD_WITHOUT_KF
|
#ifdef BUILD_WITHOUT_KF
|
||||||
# include "ui/nokde/kautosavefile.h"
|
# include "ui/nokde/kautosavefile.h"
|
||||||
#else
|
#else
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
class Diagram;
|
class Diagram;
|
||||||
class Element;
|
class Element;
|
||||||
@@ -119,6 +121,22 @@ class QETProject : public QObject
|
|||||||
QUuid derivedItemUuid(const QString &kind, const QString &key);
|
QUuid derivedItemUuid(const QString &kind, const QString &key);
|
||||||
ProjectState state() const;
|
ProjectState state() const;
|
||||||
QList<Diagram *> diagrams() 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;
|
int folioIndex(const Diagram *) const;
|
||||||
XmlElementCollection *embeddedElementCollection()const;
|
XmlElementCollection *embeddedElementCollection()const;
|
||||||
TitleBlockTemplatesProjectCollection *embeddedTitleBlockTemplatesCollection();
|
TitleBlockTemplatesProjectCollection *embeddedTitleBlockTemplatesCollection();
|
||||||
@@ -240,6 +258,18 @@ class QETProject : public QObject
|
|||||||
const QString &ignored_title = QString()) const;
|
const QString &ignored_title = QString()) const;
|
||||||
static QString normalizedAutoNumName(const QString &name);
|
static QString normalizedAutoNumName(const QString &name);
|
||||||
QVector<Element *> elementsUsingElementAutoNum(const QString &title) const;
|
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.
|
* @brief Renumber existing elements by element autonumbering scheme.
|
||||||
@@ -283,7 +313,8 @@ class QETProject : public QObject
|
|||||||
void autoFolioNumberingNewFolios ();
|
void autoFolioNumberingNewFolios ();
|
||||||
void autoFolioNumberingSelectedFolios(int, int, const QString&);
|
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();
|
bool close();
|
||||||
QETResult write();
|
QETResult write();
|
||||||
bool isReadOnly() const;
|
bool isReadOnly() const;
|
||||||
@@ -293,6 +324,7 @@ class QETProject : public QObject
|
|||||||
QString integrateTitleBlockTemplate(const TitleBlockTemplateLocation &, MoveTitleBlockTemplatesHandler *handler);
|
QString integrateTitleBlockTemplate(const TitleBlockTemplateLocation &, MoveTitleBlockTemplatesHandler *handler);
|
||||||
bool usesElement(const ElementsLocation &) const;
|
bool usesElement(const ElementsLocation &) const;
|
||||||
QList <ElementsLocation> unusedElements() const;
|
QList <ElementsLocation> unusedElements() const;
|
||||||
|
QList<ElementsLocation> unbuiltFolioElements() const;
|
||||||
bool usesTitleBlockTemplate(const TitleBlockTemplateLocation &);
|
bool usesTitleBlockTemplate(const TitleBlockTemplateLocation &);
|
||||||
bool projectWasModified();
|
bool projectWasModified();
|
||||||
bool projectOptionsWereModified();
|
bool projectOptionsWereModified();
|
||||||
@@ -474,6 +506,24 @@ class QETProject : public QObject
|
|||||||
/// saved without an id (written before ids existed)
|
/// saved without an id (written before ids existed)
|
||||||
bool m_legacy_element_autonums = false;
|
bool m_legacy_element_autonums = false;
|
||||||
void linkElementsToElementAutoNums();
|
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;
|
bool m_auto_conductor = true;
|
||||||
WireHops::Mode m_wire_hops = WireHops::Mode::None;
|
WireHops::Mode m_wire_hops = WireHops::Mode::None;
|
||||||
/// Texts drawn in a turned symbol stay horizontal (on for a new
|
/// Texts drawn in a turned symbol stay horizontal (on for a new
|
||||||
|
|||||||
@@ -150,8 +150,9 @@ void DiagramBgColorToolButton::applyColor(const QColor &color)
|
|||||||
if (!editor) {
|
if (!editor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//A folio not built yet takes the colour when it is drawn
|
||||||
for (ProjectView *pv : editor->openedProjects())
|
for (ProjectView *pv : editor->openedProjects())
|
||||||
for (Diagram *d : pv->project()->diagrams())
|
for (Diagram *d : pv->project()->folios())
|
||||||
d->update();
|
d->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,8 +176,9 @@ void DiagramBgColorToolButton::applySystemColor()
|
|||||||
if (!editor) {
|
if (!editor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//A folio not built yet takes the colour when it is drawn
|
||||||
for (ProjectView *pv : editor->openedProjects())
|
for (ProjectView *pv : editor->openedProjects())
|
||||||
for (Diagram *d : pv->project()->diagrams())
|
for (Diagram *d : pv->project()->folios())
|
||||||
d->update();
|
d->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ void DiagramEditorHandlerSizeWidget::on_comboBox_currentIndexChanged(int index)
|
|||||||
{
|
{
|
||||||
editor_->setProperty("graphics_handler_size", size);
|
editor_->setProperty("graphics_handler_size", size);
|
||||||
for (auto project_view : editor_->openedProjects()) {
|
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()) {
|
for (const auto item : diagram->items()) {
|
||||||
if (item->type() == QetGraphicsHandlerItem::Type) {
|
if (item->type() == QetGraphicsHandlerItem::Type) {
|
||||||
auto handler = qgraphicsitem_cast<QetGraphicsHandlerItem *>(item);
|
auto handler = qgraphicsitem_cast<QetGraphicsHandlerItem *>(item);
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ void JumpToElementDialog::activateCurrentItem()
|
|||||||
const int index = current->data(Qt::UserRole).toInt();
|
const int index = current->data(Qt::UserRole).toInt();
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
const QList<Diagram *> diagrams = m_diagram->project()
|
const QList<Diagram *> diagrams = m_diagram->project()
|
||||||
? m_diagram->project()->diagrams()
|
? m_diagram->project()->folios()
|
||||||
: QList<Diagram *>();
|
: QList<Diagram *>();
|
||||||
const int folio = current->data(Qt::UserRole + 2).toInt();
|
const int folio = current->data(Qt::UserRole + 2).toInt();
|
||||||
if (folio >= 0 && folio < diagrams.size()) {
|
if (folio >= 0 && folio < diagrams.size()) {
|
||||||
@@ -208,7 +208,8 @@ QListWidgetItem *JumpToElementDialog::cellItem(const QString &needle) const
|
|||||||
if (!m_diagram || !m_diagram->project()) {
|
if (!m_diagram || !m_diagram->project()) {
|
||||||
return nullptr;
|
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;
|
Diagram *diagram = m_diagram;
|
||||||
QString cell = needle;
|
QString cell = needle;
|
||||||
|
|||||||
@@ -87,6 +87,13 @@ if(Python3_Interpreter_FOUND AND CMAKE_GENERATOR STREQUAL "Ninja")
|
|||||||
--source ${CMAKE_CURRENT_SOURCE_DIR}/attached_plain_paste_probe.cpp
|
--source ${CMAKE_CURRENT_SOURCE_DIR}/attached_plain_paste_probe.cpp
|
||||||
--fixture ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/unlinked_contact_label.qet)
|
--fixture ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/unlinked_contact_label.qet)
|
||||||
set_tests_properties(tst_attachedplainpaste PROPERTIES TIMEOUT 300 RUN_SERIAL TRUE)
|
set_tests_properties(tst_attachedplainpaste PROPERTIES TIMEOUT 300 RUN_SERIAL TRUE)
|
||||||
|
add_test(NAME tst_lazyfolios
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_lazy_folios_probe.py
|
||||||
|
--build ${CMAKE_BINARY_DIR} --binary $<TARGET_FILE:qelectrotech>
|
||||||
|
--source ${CMAKE_CURRENT_SOURCE_DIR}/lazy_folios_probe.cpp
|
||||||
|
--fixture ${CMAKE_CURRENT_SOURCE_DIR}/../../examples/industrial.qet)
|
||||||
|
set_tests_properties(tst_lazyfolios PROPERTIES TIMEOUT 600 RUN_SERIAL TRUE
|
||||||
|
ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
|
||||||
# Deleting a cable, undoing the delete twice and then editing anything
|
# Deleting a cable, undoing the delete twice and then editing anything
|
||||||
# has both cable commands tearing down the same cable. Runs under the
|
# has both cable commands tearing down the same cable. Runs under the
|
||||||
# same objects the application uses, so a second free is caught.
|
# same objects the application uses, so a second free is caught.
|
||||||
|
|||||||
@@ -0,0 +1,579 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
// Lazy folios (QET_LAZY_FOLIOS, LAZY-FOLIO-PLAN.md stages 5.2 and 5.3),
|
||||||
|
// linked against the application's objects: a folio shown in a project
|
||||||
|
// opened without building its folios draws and links as in the project
|
||||||
|
// opened as usual, building only it and the folios its links and tables
|
||||||
|
// reach, and the project saves the same.
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFontDatabase>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QSqlRecord>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "../../sources/diagram.h"
|
||||||
|
#include "../../sources/qetproject.h"
|
||||||
|
#include "../../sources/qetresult.h"
|
||||||
|
#include "../../sources/qetmessagebox.h"
|
||||||
|
#include "../../sources/qetgraphicsitem/element.h"
|
||||||
|
#include "../../sources/autoNum/elementautonumschemecommand.h"
|
||||||
|
#include "../../sources/elementprovider.h"
|
||||||
|
#include "../../sources/TerminalStrip/terminalstrip.h"
|
||||||
|
#include "../../sources/ui/elementpropertieswidget.h"
|
||||||
|
#include "../../sources/conductorautonumerotation.h"
|
||||||
|
#include "../../sources/diagramcontent.h"
|
||||||
|
#include "../../sources/diagramview.h"
|
||||||
|
#include "../../sources/diagramevent/diagrameventaddpaste.h"
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include "../../sources/qetgraphicsitem/conductor.h"
|
||||||
|
#include "../../sources/qetgraphicsitem/terminal.h"
|
||||||
|
#include "../../sources/undocommand/changeelementinformationcommand.h"
|
||||||
|
#include "../../sources/undocommand/changetitleblockcommand.h"
|
||||||
|
#include "../../sources/undocommand/deleteqgraphicsitemcommand.h"
|
||||||
|
#include "../../sources/factory/elementfactory.h"
|
||||||
|
#include "../../sources/undocommand/addgraphicsobjectcommand.h"
|
||||||
|
|
||||||
|
static void check(bool value, const char *message) { if (!value) throw std::runtime_error(message); }
|
||||||
|
|
||||||
|
//Every symbol's numbering, in a stable order
|
||||||
|
static QStringList numbering(const QETProject &project)
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
for (const auto &symbol : project.symbolNumbering())
|
||||||
|
list << QStringList{symbol.label, symbol.formula, symbol.scheme.toString(),
|
||||||
|
symbol.sequence.unit.join(';'), symbol.sequence.ten.join(';'),
|
||||||
|
symbol.sequence.hundred.join(';'),
|
||||||
|
QString::number(symbol.numbered)}.join('|');
|
||||||
|
list.sort();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
//What the scheme @p title tells of the numbers its symbols carry
|
||||||
|
static QString gaps(const QETProject &project, const QString &title)
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
for (const auto &gap : ElementAutoNumSchemeCommand::gapRanges(&project, title))
|
||||||
|
list << QString("%1-%2").arg(gap.from).arg(gap.to);
|
||||||
|
return list.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
//A symbol of @p location placed on folio @p i as the folio's editor
|
||||||
|
//places one (DiagramEventAddElement::addElement()), numbered with the
|
||||||
|
//scheme @p title: its label, and the numbers then free for it
|
||||||
|
static QString place(QETProject &project, int i, const QString &location, const QString &title)
|
||||||
|
{
|
||||||
|
Diagram *folio = project.folios().at(i);
|
||||||
|
project.buildFolioToShow(folio);
|
||||||
|
project.setCurrrentElementAutonum(title);
|
||||||
|
int state = 0;
|
||||||
|
Element *element = ElementFactory::Instance()->createElement(
|
||||||
|
ElementsLocation(location, &project), nullptr, &state);
|
||||||
|
check(!state, "symbol made");
|
||||||
|
element->setPos(QPointF(-500, -500));
|
||||||
|
folio->addItem(element);
|
||||||
|
auto *undo = new QUndoCommand("insert");
|
||||||
|
new AddGraphicsObjectCommand(element, folio, element->pos(), undo);
|
||||||
|
element->setUpFormula(true, undo);
|
||||||
|
folio->undoStack().push(undo);
|
||||||
|
element->freezeNewAddedElement();
|
||||||
|
QStringList free;
|
||||||
|
for (int n : ElementAutoNumSchemeCommand::freeNumbers(&project, title, element))
|
||||||
|
free << QString::number(n);
|
||||||
|
return element->actualLabel() + " free " + free.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
//The symbols a search of a project finds, by uuid
|
||||||
|
static QStringList found(const QVector<QPointer<Element>> &elements)
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
for (const auto &element : elements)
|
||||||
|
list << element->uuid().toString();
|
||||||
|
list.sort();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Everyday edits on folio @p i, as its editor makes them: a symbol's
|
||||||
|
//information, a wire, the folio's title block, a linked symbol
|
||||||
|
//deleted, then one undo and one redo
|
||||||
|
static void edit(QETProject &project, int i)
|
||||||
|
{
|
||||||
|
Diagram *folio = project.folios().at(i);
|
||||||
|
project.buildFolioToShow(folio);
|
||||||
|
QList<Element *> elements = folio->elements();
|
||||||
|
std::sort(elements.begin(), elements.end(), [](Element *a, Element *b) {
|
||||||
|
return a->uuid() < b->uuid(); });
|
||||||
|
check(elements.size() > 2, "a folio with symbols to edit");
|
||||||
|
|
||||||
|
const DiagramContext old_info = elements.first()->elementInformations();
|
||||||
|
DiagramContext new_info = old_info;
|
||||||
|
new_info.addValue(QStringLiteral("comment"), QStringLiteral("edited lazily"));
|
||||||
|
folio->undoStack().push(new ChangeElementInformationCommand(elements.first(), old_info, new_info));
|
||||||
|
|
||||||
|
Terminal *ends[2] = {nullptr, nullptr};
|
||||||
|
for (Element *element : elements) {
|
||||||
|
for (Terminal *terminal : element->terminals()) {
|
||||||
|
if (terminal->conductors().isEmpty()
|
||||||
|
&& (!ends[0] || ends[0]->parentElement() != element)) {
|
||||||
|
ends[ends[0] ? 1 : 0] = terminal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ends[1]) break;
|
||||||
|
}
|
||||||
|
if (ends[1]) {
|
||||||
|
auto *undo = new QUndoCommand(QStringLiteral("wire"));
|
||||||
|
auto *wire = new Conductor(ends[0], ends[1]);
|
||||||
|
new AddGraphicsObjectCommand(wire, folio, QPointF(), undo);
|
||||||
|
ConductorAutoNumerotation numbering(wire, folio, undo);
|
||||||
|
numbering.numerate();
|
||||||
|
folio->undoStack().push(undo);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TitleBlockProperties old_block = folio->border_and_titleblock.exportTitleBlock();
|
||||||
|
TitleBlockProperties new_block = old_block;
|
||||||
|
new_block.title += QStringLiteral(" (edited)");
|
||||||
|
folio->undoStack().push(new ChangeTitleBlockCommand(folio, old_block, new_block));
|
||||||
|
|
||||||
|
//Copy three symbols and paste them, as Ctrl+C then Ctrl+Shift+V
|
||||||
|
//and Return do
|
||||||
|
{
|
||||||
|
DiagramView view(folio);
|
||||||
|
folio->clearSelection();
|
||||||
|
for (int n = 0 ; n < 3 ; ++n)
|
||||||
|
elements.at(n)->setSelected(true);
|
||||||
|
view.copy();
|
||||||
|
auto *paste = new DiagramEventAddPaste(folio, QPointF(), DiagramEventAddPaste::AtOrigin);
|
||||||
|
folio->setEventInterface(paste);
|
||||||
|
QKeyEvent drop(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier);
|
||||||
|
paste->keyPressEvent(&drop);
|
||||||
|
folio->clearEventInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *linked = nullptr;
|
||||||
|
for (Element *element : elements)
|
||||||
|
if (!element->linkedElements().isEmpty()) { linked = element; break; }
|
||||||
|
folio->clearSelection();
|
||||||
|
(linked ? linked : elements.last())->setSelected(true);
|
||||||
|
folio->undoStack().push(new DeleteQGraphicsItemCommand(folio, DiagramContent(folio, true)));
|
||||||
|
|
||||||
|
project.undoStack()->undo();
|
||||||
|
project.undoStack()->redo();
|
||||||
|
}
|
||||||
|
|
||||||
|
//The project as saving writes it: folios not built written as read
|
||||||
|
//when building them would write the same (QETProject::toXml())
|
||||||
|
static QString saved(QETProject &project)
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> kept;
|
||||||
|
const QDomDocument document = project.toXml(&kept);
|
||||||
|
return QETProject::spliceKeptFolios(document.toString(4), kept);
|
||||||
|
}
|
||||||
|
|
||||||
|
//The rows of the project database's tables, in their order, read as
|
||||||
|
//every caller reads them (newQuery(): brought up to date first)
|
||||||
|
static QStringList tables(QETProject &project)
|
||||||
|
{
|
||||||
|
QStringList rows;
|
||||||
|
for (const char *table : {"diagram", "diagram_info", "element", "element_info",
|
||||||
|
"terminal", "conductor", "link"}) {
|
||||||
|
QSqlQuery query = project.dataBase()->newQuery(
|
||||||
|
QStringLiteral("SELECT * FROM %1 ORDER BY rowid").arg(QLatin1String(table)));
|
||||||
|
check(query.exec(), "a table read");
|
||||||
|
QStringList table_rows;
|
||||||
|
while (query.next()) {
|
||||||
|
QStringList row;
|
||||||
|
for (int i = 0 ; i < query.record().count() ; ++i)
|
||||||
|
row << query.value(i).toString();
|
||||||
|
table_rows << QLatin1String(table) + ": " + row.join('|');
|
||||||
|
}
|
||||||
|
//Rows are written in the order the scene gives a folio's items,
|
||||||
|
//which is not the same from one opening to the next: two usual
|
||||||
|
//opens given the same edits differ in it (637 rows of 3 394 on
|
||||||
|
//industrial.qet). What a table holds is compared, not its order
|
||||||
|
table_rows.sort();
|
||||||
|
rows << table_rows;
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QImage render(Diagram *folio)
|
||||||
|
{
|
||||||
|
const QRectF rect = folio->border_and_titleblock.borderAndTitleBlockRect().adjusted(-5, -5, 5, 5);
|
||||||
|
QImage image(rect.size().toSize(), QImage::Format_RGB32);
|
||||||
|
image.fill(Qt::white);
|
||||||
|
QPainter painter(&image);
|
||||||
|
folio->render(&painter, QRectF(QPointF(0, 0), rect.size()), rect);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Each symbol's partners, in their order
|
||||||
|
static QStringList links(Diagram *folio)
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
for (Element *element : folio->elements()) {
|
||||||
|
QStringList partners;
|
||||||
|
for (Element *partner : element->linkedElements())
|
||||||
|
partners << partner->uuid().toString();
|
||||||
|
list << element->uuid().toString() + ' ' + partners.join(',');
|
||||||
|
}
|
||||||
|
list.sort();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QApplication app(argc, argv); std::freopen(argv[2], "w", stdout);
|
||||||
|
QTemporaryDir settings; QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||||
|
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, settings.path());
|
||||||
|
QCoreApplication::setOrganizationName("QETLazyFoliosRegression");
|
||||||
|
QETProject::setBackupEnabled(false); QET::QetMessageBox::setNonInteractive(true);
|
||||||
|
QFontDatabase::addApplicationFont(":/fonts/LiberationSans-Regular.ttf");
|
||||||
|
//Folios not built saved as read (QETProject::toXml()): this
|
||||||
|
//fixture is written by this version's own save
|
||||||
|
qputenv("QET_LAZY_SAVE_KEPT", "1");
|
||||||
|
try {
|
||||||
|
const QString output = QString::fromLocal8Bit(argv[3]);
|
||||||
|
qunsetenv("QET_LAZY_FOLIOS");
|
||||||
|
//A file saved by this version: its folios carry their uuids,
|
||||||
|
//which opening without building them needs
|
||||||
|
const QString path = output + "/lazy_folios.qet";
|
||||||
|
QSet<int> strip_folios;
|
||||||
|
{
|
||||||
|
QETProject source(QString::fromLocal8Bit(argv[1]));
|
||||||
|
check(source.state() == QETProject::Ok, "fixture opens");
|
||||||
|
//A terminal strip of terminals on several folios: it is
|
||||||
|
//read with the project, before any folio is built
|
||||||
|
TerminalStrip *strip = source.newTerminalStrip(QStringLiteral("=A1"), QStringLiteral("+B1"),
|
||||||
|
QStringLiteral("X99"));
|
||||||
|
int added = 0;
|
||||||
|
for (Diagram *folio : source.folios())
|
||||||
|
for (Element *element : folio->elements())
|
||||||
|
if (element->elementData().m_type == ElementData::Terminal && added < 12
|
||||||
|
&& strip->addTerminal(element)) {
|
||||||
|
++added;
|
||||||
|
strip_folios.insert(source.folioIndex(folio));
|
||||||
|
}
|
||||||
|
check(added > 3, "a terminal strip with terminals");
|
||||||
|
source.setFilePath(path);
|
||||||
|
check(source.write().isOk(), "fixture saved by this version");
|
||||||
|
}
|
||||||
|
QETProject eager(path);
|
||||||
|
check(eager.state() == QETProject::Ok, "project opens");
|
||||||
|
{
|
||||||
|
qputenv("QET_LAZY_FOLIOS", "1");
|
||||||
|
QETProject lazy_strips(path);
|
||||||
|
qunsetenv("QET_LAZY_FOLIOS");
|
||||||
|
auto strip_of = [](const QETProject &project) {
|
||||||
|
for (TerminalStrip *strip : project.terminalStrip())
|
||||||
|
if (strip->name() == QStringLiteral("X99")) return strip->physicalTerminalCount();
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
check(strip_of(lazy_strips) > 3 && strip_of(lazy_strips) == strip_of(eager),
|
||||||
|
"a terminal strip keeps its terminals when the folios are not built");
|
||||||
|
}
|
||||||
|
const QList<Diagram *> folios = eager.folios();
|
||||||
|
check(folios.size() > 3, "several folios");
|
||||||
|
QList<QImage> images; QList<QStringList> partners;
|
||||||
|
int linked = 0;
|
||||||
|
for (Diagram *folio : folios) {
|
||||||
|
images << render(folio);
|
||||||
|
partners << links(folio);
|
||||||
|
for (Element *element : folio->elements())
|
||||||
|
linked += !element->linkedElements().isEmpty();
|
||||||
|
}
|
||||||
|
check(linked > 0, "the project has linked symbols");
|
||||||
|
|
||||||
|
qputenv("QET_LAZY_FOLIOS", "1");
|
||||||
|
auto same = [&](QETProject &lazy, int i, const char *what) {
|
||||||
|
Diagram *folio = lazy.folios().at(i);
|
||||||
|
lazy.buildFolioToShow(folio);
|
||||||
|
if (render(folio) != images.at(i) || links(folio) != partners.at(i)) {
|
||||||
|
std::printf("folio %d, %s: differs\n", i + 1, what);
|
||||||
|
throw std::runtime_error("a folio shown lazily draws or links as opened as usual");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//Each folio first after opening
|
||||||
|
for (int i = 0; i < folios.size(); ++i) {
|
||||||
|
QETProject lazy(path);
|
||||||
|
check(lazy.unloadedFolioCount() == folios.size() - strip_folios.size(),
|
||||||
|
"opened building only the folios of the terminal strip's terminals");
|
||||||
|
same(lazy, i, "shown first");
|
||||||
|
check(lazy.unloadedFolioCount() > 0, "showing a folio does not build them all");
|
||||||
|
}
|
||||||
|
//Every folio, one after the other, then the save
|
||||||
|
QETProject lazy(path);
|
||||||
|
for (int i = 0; i < folios.size(); ++i)
|
||||||
|
same(lazy, i, "shown in turn");
|
||||||
|
check(lazy.toXml().toString() == eager.toXml().toString(), "saves as opened as usual");
|
||||||
|
check(lazy.undoStack()->isClean(), "showing folios changes nothing");
|
||||||
|
|
||||||
|
//Numbering asks every symbol of the project: it is answered for
|
||||||
|
//the folios not built without building them, as when built
|
||||||
|
QETProject numbered(path);
|
||||||
|
check(numbering(numbered) == numbering(eager), "symbols' numbering as when built");
|
||||||
|
const QStringList titles = eager.elementAutoNum().keys();
|
||||||
|
check(!titles.isEmpty(), "the project has element numbering schemes");
|
||||||
|
for (const QString &title : titles)
|
||||||
|
check(gaps(numbered, title) == gaps(eager, title), "a scheme's gaps as when built");
|
||||||
|
check(numbered.unloadedFolioCount() == folios.size() - strip_folios.size(), "numbering read without building");
|
||||||
|
//A symbol placed on a folio, numbered by a scheme others follow
|
||||||
|
QString location, title;
|
||||||
|
int folio = -1;
|
||||||
|
for (const auto &symbol : eager.symbolNumbering()) {
|
||||||
|
if (symbol.numbered && symbol.element && !eager.elementAutoNumTitle(symbol.scheme).isEmpty()) {
|
||||||
|
location = symbol.element->location().toString();
|
||||||
|
title = eager.elementAutoNumTitle(symbol.scheme);
|
||||||
|
folio = eager.folios().indexOf(symbol.element->diagram());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check(folio >= 0, "a symbol following a scheme");
|
||||||
|
const QString placed = place(numbered, folio, location, title);
|
||||||
|
std::printf("placed on folio %d with %s: %s\n", folio + 1, qPrintable(title), qPrintable(placed));
|
||||||
|
check(numbered.unloadedFolioCount() > 0, "numbering a placed symbol does not build every folio");
|
||||||
|
QETProject usual(path);
|
||||||
|
check(placed == place(usual, folio, location, title), "a placed symbol numbered as when built");
|
||||||
|
//The candidates a link tab lists (ElementProvider): found as
|
||||||
|
//when built, building only the folios which hold some
|
||||||
|
const ElementProvider usual_search(&eager);
|
||||||
|
for (const ElementData::Types kinds : {ElementData::Types(ElementData::Slave),
|
||||||
|
ElementData::Types(ElementData::Master),
|
||||||
|
ElementData::Types(ElementData::AllReport),
|
||||||
|
ElementData::Types(ElementData::Terminal)}) {
|
||||||
|
QETProject searched(path);
|
||||||
|
const ElementProvider search(&searched);
|
||||||
|
const QStringList lazy_free = found(search.freeElement(kinds)), usual_free = found(usual_search.freeElement(kinds));
|
||||||
|
if (lazy_free != usual_free) {
|
||||||
|
for (const QString &u : usual_free) if (!lazy_free.contains(u)) std::printf(" only when built: %s\n", qPrintable(u));
|
||||||
|
for (const QString &u : lazy_free) if (!usual_free.contains(u)) std::printf(" only lazily: %s\n", qPrintable(u));
|
||||||
|
}
|
||||||
|
check(lazy_free == usual_free, "free symbols found as when built");
|
||||||
|
std::printf("free symbols of kinds %d: %d folios built\n", int(kinds),
|
||||||
|
int(folios.size() - searched.unloadedFolioCount()));
|
||||||
|
check(found(search.find(kinds)) == found(usual_search.find(kinds)),
|
||||||
|
"symbols found as when built");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
QETProject searched(path);
|
||||||
|
ElementProvider(&searched).freeElement(ElementData::Slave);
|
||||||
|
check(searched.unloadedFolioCount() > 0, "a search for free contacts does not build every folio");
|
||||||
|
}
|
||||||
|
//Selecting a symbol shows its properties, a link tab for a
|
||||||
|
//coil, a contact or a report: the folio and few others built
|
||||||
|
for (const Element::kind kind : {Element::Master, Element::Slave, Element::NextReport,
|
||||||
|
Element::PreviousReport}) {
|
||||||
|
QETProject selected(path);
|
||||||
|
Element *element = nullptr;
|
||||||
|
for (int i = 0 ; !element && i < folios.size() ; ++i) {
|
||||||
|
for (Element *candidate : eager.folios().at(i)->elements()) {
|
||||||
|
if (candidate->linkType() == kind) {
|
||||||
|
selected.buildFolioToShow(selected.folios().at(i));
|
||||||
|
for (Element *e : selected.folios().at(i)->elements())
|
||||||
|
if (e->uuid() == candidate->uuid()) element = e;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check(element, "a symbol of each kind with a link tab");
|
||||||
|
const int before = selected.unloadedFolioCount();
|
||||||
|
delete new ElementPropertiesWidget(element);
|
||||||
|
std::printf("properties of a symbol of kind %d: %d more folios built, %d not built\n",
|
||||||
|
int(kind), before - selected.unloadedFolioCount(), selected.unloadedFolioCount());
|
||||||
|
check(selected.unloadedFolioCount() > 0, "selecting a symbol does not build every folio");
|
||||||
|
}
|
||||||
|
//Everyday edits on a folio linked to others: the others stay
|
||||||
|
//not built, and the project saves as when they are
|
||||||
|
int busiest = 0, most = -1;
|
||||||
|
for (int i = 0 ; i < folios.size() ; ++i) {
|
||||||
|
int partners = 0;
|
||||||
|
for (Element *element : folios.at(i)->elements())
|
||||||
|
for (Element *partner : element->linkedElements())
|
||||||
|
partners += partner->diagram() != folios.at(i);
|
||||||
|
if (partners > most) { most = partners; busiest = i; }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
QETProject edited(path), usual(path);
|
||||||
|
qunsetenv("QET_LAZY_FOLIOS");
|
||||||
|
QETProject usual_edited(path);
|
||||||
|
qputenv("QET_LAZY_FOLIOS", "1");
|
||||||
|
edit(edited, busiest);
|
||||||
|
std::printf("edits on folio %d (%d links to other folios): %d folios not built\n",
|
||||||
|
busiest + 1, most, edited.unloadedFolioCount());
|
||||||
|
check(edited.unloadedFolioCount() > 0, "editing a folio does not build every folio");
|
||||||
|
edit(usual_edited, busiest);
|
||||||
|
//Made by the edits, a new wire's uuid is random: any uuid the
|
||||||
|
//file had not is "new" before comparing
|
||||||
|
QFile saved(path); saved.open(QIODevice::ReadOnly);
|
||||||
|
const QString before = QString::fromUtf8(saved.readAll());
|
||||||
|
static const QRegularExpression uuid_re(QStringLiteral("uuid=\"(\\{[0-9a-f-]{36}\\})\""));
|
||||||
|
auto known = [&before](QString xml) {
|
||||||
|
QString out;
|
||||||
|
qsizetype at = 0;
|
||||||
|
for (auto it = uuid_re.globalMatch(xml) ; it.hasNext() ; ) {
|
||||||
|
const auto match = it.next();
|
||||||
|
out += xml.mid(at, match.capturedStart() - at);
|
||||||
|
out += before.contains(match.captured(1)) ? match.captured(0)
|
||||||
|
: QStringLiteral("uuid=\"new\"");
|
||||||
|
at = match.capturedEnd();
|
||||||
|
}
|
||||||
|
return out + xml.mid(at);
|
||||||
|
};
|
||||||
|
const QStringList lazy_rows = tables(edited), usual_rows = tables(usual_edited);
|
||||||
|
static const QRegularExpression bare_uuid_re(QStringLiteral("\\{[0-9a-f-]{36}\\}"));
|
||||||
|
auto known_row = [&before](QString row) {
|
||||||
|
for (auto it = bare_uuid_re.globalMatch(QString(row)) ; it.hasNext() ; ) {
|
||||||
|
const QString uuid = it.next().captured(0);
|
||||||
|
if (!before.contains(uuid)) row.replace(uuid, QStringLiteral("new"));
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
};
|
||||||
|
check(edited.unloadedFolioCount() > 0, "reading the database does not build every folio");
|
||||||
|
QStringList lazy_known, usual_known;
|
||||||
|
for (const QString &row : lazy_rows) lazy_known << known_row(row);
|
||||||
|
for (const QString &row : usual_rows) usual_known << known_row(row);
|
||||||
|
lazy_known.sort();
|
||||||
|
usual_known.sort();
|
||||||
|
int differing = 0;
|
||||||
|
for (int i = 0 ; i < std::max(lazy_known.size(), usual_known.size()) ; ++i)
|
||||||
|
if (lazy_known.value(i) != usual_known.value(i) && differing++ < 5)
|
||||||
|
std::printf(" row %d: lazy %s\n usual %s\n", i, qPrintable(lazy_known.value(i).left(160)),
|
||||||
|
qPrintable(usual_known.value(i).left(160)));
|
||||||
|
std::printf("database after the edits: %d rows, %d differ\n", int(usual_rows.size()), differing);
|
||||||
|
check(!differing, "the database after the edits as when every folio is built");
|
||||||
|
//A pasted copy has a random uuid, and a folio's symbols are
|
||||||
|
//saved in uuid order: the lines are compared, not their order
|
||||||
|
auto lines = [&known](const QString &xml) {
|
||||||
|
QStringList list = known(xml).split(QLatin1Char('\n'));
|
||||||
|
list.sort();
|
||||||
|
return list.join(QLatin1Char('\n'));
|
||||||
|
};
|
||||||
|
const QString a = lines(edited.toXml().toString()), b = lines(usual_edited.toXml().toString());
|
||||||
|
if (a != b) {
|
||||||
|
QFile fa(output + "/edited-lazy.xml"); fa.open(QIODevice::WriteOnly); fa.write(a.toUtf8());
|
||||||
|
QFile fb(output + "/edited-usual.xml"); fb.open(QIODevice::WriteOnly); fb.write(b.toUtf8());
|
||||||
|
}
|
||||||
|
check(a == b,
|
||||||
|
"edits save as when every folio is built");
|
||||||
|
}
|
||||||
|
//Saving writes the folios not built as they were read, the same
|
||||||
|
//bytes as building them would write
|
||||||
|
const QString usual_text = saved(eager);
|
||||||
|
{
|
||||||
|
QETProject untouched(path);
|
||||||
|
const int unbuilt = untouched.unloadedFolioCount();
|
||||||
|
const QString untouched_text = saved(untouched);
|
||||||
|
if (untouched_text != usual_text) {
|
||||||
|
QFile fa(output + "/untouched-lazy.xml"); fa.open(QIODevice::WriteOnly); fa.write(untouched_text.toUtf8());
|
||||||
|
QFile fb(output + "/untouched-usual.xml"); fb.open(QIODevice::WriteOnly); fb.write(usual_text.toUtf8());
|
||||||
|
}
|
||||||
|
check(untouched_text == usual_text, "a project saved without building, as when built");
|
||||||
|
//Only the partners of the folios the terminal strip built
|
||||||
|
std::printf("saved untouched: %d of %d folios not built built to save\n",
|
||||||
|
unbuilt - untouched.unloadedFolioCount(), unbuilt);
|
||||||
|
check(untouched.unloadedFolioCount() > 0, "saving does not build every folio");
|
||||||
|
}
|
||||||
|
//After everyday edits on a folio: as when built, others not built
|
||||||
|
{
|
||||||
|
QETProject edited(path), usual(path);
|
||||||
|
qunsetenv("QET_LAZY_FOLIOS");
|
||||||
|
QETProject usual_edited(path);
|
||||||
|
qputenv("QET_LAZY_FOLIOS", "1");
|
||||||
|
edit(edited, busiest);
|
||||||
|
edit(usual_edited, busiest);
|
||||||
|
QFile file(path); file.open(QIODevice::ReadOnly);
|
||||||
|
const QString before = QString::fromUtf8(file.readAll());
|
||||||
|
static const QRegularExpression uuid_re(QStringLiteral("\\{[0-9a-f-]{36}\\}"));
|
||||||
|
auto lines = [&before](QString text) {
|
||||||
|
for (auto it = uuid_re.globalMatch(QString(text)) ; it.hasNext() ; ) {
|
||||||
|
const QString uuid = it.next().captured(0);
|
||||||
|
if (!before.contains(uuid)) text.replace(uuid, QStringLiteral("new"));
|
||||||
|
}
|
||||||
|
QStringList list = text.split(QLatin1Char('\n'));
|
||||||
|
list.sort();
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
const QStringList la = lines(saved(edited)), lb = lines(saved(usual_edited));
|
||||||
|
if (la != lb) {
|
||||||
|
int shown = 0;
|
||||||
|
for (const QString &l : lb) if (!la.contains(l) && shown++ < 6) std::printf(" only usual: %s\n", qPrintable(l.trimmed().left(150)));
|
||||||
|
shown = 0;
|
||||||
|
for (const QString &l : la) if (!lb.contains(l) && shown++ < 6) std::printf(" only lazy: %s\n", qPrintable(l.trimmed().left(150)));
|
||||||
|
}
|
||||||
|
check(la == lb, "edits saved without building, as when built");
|
||||||
|
std::printf("saved after the edits: %d folios not built\n", edited.unloadedFolioCount());
|
||||||
|
check(edited.unloadedFolioCount() > 0, "saving after edits does not build every folio");
|
||||||
|
}
|
||||||
|
//A folio not built given a new title: it and the folios its
|
||||||
|
//links reach are built to be saved, the others not
|
||||||
|
{
|
||||||
|
QETProject renamed(path);
|
||||||
|
qunsetenv("QET_LAZY_FOLIOS");
|
||||||
|
QETProject usual_renamed(path);
|
||||||
|
qputenv("QET_LAZY_FOLIOS", "1");
|
||||||
|
for (QETProject *project : {&renamed, &usual_renamed}) {
|
||||||
|
Diagram *folio = project->folios().at(busiest);
|
||||||
|
TitleBlockProperties block = folio->border_and_titleblock.exportTitleBlock();
|
||||||
|
block.title += QStringLiteral(" (renamed)");
|
||||||
|
folio->border_and_titleblock.importTitleBlock(block);
|
||||||
|
}
|
||||||
|
check(saved(renamed) == saved(usual_renamed), "a renamed folio saved as when built");
|
||||||
|
std::printf("saved after a folio not built was renamed: %d folios not built\n", renamed.unloadedFolioCount());
|
||||||
|
check(renamed.unloadedFolioCount() > 0, "renaming a folio does not build every folio to save");
|
||||||
|
}
|
||||||
|
//Project properties, which any folio's texts can use: every
|
||||||
|
//folio is built to be saved
|
||||||
|
{
|
||||||
|
QETProject changed(path);
|
||||||
|
qunsetenv("QET_LAZY_FOLIOS");
|
||||||
|
QETProject usual_changed(path);
|
||||||
|
qputenv("QET_LAZY_FOLIOS", "1");
|
||||||
|
for (QETProject *project : {&changed, &usual_changed}) {
|
||||||
|
DiagramContext properties = project->projectProperties();
|
||||||
|
properties.addValue(QStringLiteral("lazy_test"), QStringLiteral("1"));
|
||||||
|
project->setProjectProperties(properties);
|
||||||
|
}
|
||||||
|
check(saved(changed) == saved(usual_changed), "changed project properties saved as when built");
|
||||||
|
check(changed.unloadedFolioCount() == 0, "changed project properties build every folio to save");
|
||||||
|
}
|
||||||
|
//A potential through folio reports is followed as far as when
|
||||||
|
//every folio is built
|
||||||
|
{
|
||||||
|
Conductor *usual_wire = nullptr;
|
||||||
|
for (Diagram *folio : eager.folios())
|
||||||
|
for (Element *element : folio->elements())
|
||||||
|
if ((element->linkType() & Element::AllReport) && !element->conductors().isEmpty()
|
||||||
|
&& !usual_wire)
|
||||||
|
usual_wire = element->conductors().first();
|
||||||
|
check(usual_wire, "a wire on a folio report");
|
||||||
|
auto potential = [](Conductor *wire) {
|
||||||
|
QStringList list;
|
||||||
|
for (Conductor *c : wire->relatedPotentialConductors(true))
|
||||||
|
list << c->uuid().toString();
|
||||||
|
list.sort();
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
QETProject followed(path);
|
||||||
|
Diagram *folio = followed.folios().at(eager.folioIndex(usual_wire->diagram()));
|
||||||
|
followed.buildFolioToShow(folio);
|
||||||
|
Conductor *wire = nullptr;
|
||||||
|
for (Conductor *c : folio->conductors())
|
||||||
|
if (c->uuid() == usual_wire->uuid()) wire = c;
|
||||||
|
check(wire, "the same wire");
|
||||||
|
check(potential(wire) == potential(usual_wire), "a potential followed as when built");
|
||||||
|
std::printf("potential through reports: %d wires, %d folios not built\n",
|
||||||
|
int(potential(wire).size()), followed.unloadedFolioCount());
|
||||||
|
}
|
||||||
|
std::printf("PASS: %d folios\n", int(folios.size()));
|
||||||
|
} catch (const std::exception &error) {
|
||||||
|
std::printf("FAIL: %s\n", error.what());
|
||||||
|
std::fflush(stdout);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::fflush(stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Link the lazy folios integration probe using an existing Ninja app build.
|
||||||
|
|
||||||
|
Reuses app objects without duplicating the app or introducing a core-library
|
||||||
|
refactor. No application source or existing executable is replaced. The
|
||||||
|
generated manifest, probe binary, reports and export fixtures stay in build/.
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import pathlib
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--build', required=True)
|
||||||
|
parser.add_argument('--binary', required=True)
|
||||||
|
parser.add_argument('--source', required=True)
|
||||||
|
parser.add_argument('--fixture', required=True)
|
||||||
|
args = parser.parse_args()
|
||||||
|
build = pathlib.Path(args.build).resolve()
|
||||||
|
binary = pathlib.Path(args.binary).resolve()
|
||||||
|
source = pathlib.Path(args.source).resolve()
|
||||||
|
fixture = pathlib.Path(args.fixture).resolve()
|
||||||
|
manifest = (build / 'build.ninja').read_text(encoding='utf-8')
|
||||||
|
match = re.search(r'^build (\S*?/sources/main\.cpp\.(?:obj|o)):', manifest, re.MULTILINE)
|
||||||
|
if not match:
|
||||||
|
sys.exit('Cannot find the application main object in the Ninja build.')
|
||||||
|
main_object = match.group(1)
|
||||||
|
probe_object = main_object.replace('main.cpp.', 'lazy_folios_probe.cpp.')
|
||||||
|
probe_binary = binary.with_name('lazy_folios_probe' + binary.suffix)
|
||||||
|
def escape(path):
|
||||||
|
return pathlib.Path(path).as_posix().replace('$', '$$').replace(':', '$:').replace(' ', '$ ')
|
||||||
|
main_source = source.parents[2] / 'sources' / 'main.cpp'
|
||||||
|
manifest = manifest.replace(main_object, probe_object)
|
||||||
|
manifest = manifest.replace(escape(main_source), escape(source))
|
||||||
|
if binary.suffix:
|
||||||
|
manifest = manifest.replace(binary.name, probe_binary.name)
|
||||||
|
else:
|
||||||
|
old_target = escape(binary.name if binary.parent == build else binary)
|
||||||
|
new_target = escape(probe_binary.name if probe_binary.parent == build else probe_binary)
|
||||||
|
manifest = manifest.replace('build ' + old_target + ':', 'build ' + new_target + ':')
|
||||||
|
old_file = binary.name if binary.parent == build else binary.as_posix()
|
||||||
|
new_file = probe_binary.name if probe_binary.parent == build else probe_binary.as_posix()
|
||||||
|
manifest = re.sub(r'^ TARGET_FILE = ' + re.escape(old_file) + r'$',
|
||||||
|
' TARGET_FILE = ' + new_file, manifest, flags=re.MULTILINE)
|
||||||
|
probe_manifest = build / 'lazy-folios-probe.ninja'
|
||||||
|
probe_manifest.write_text(manifest, encoding='utf-8')
|
||||||
|
ninja = shutil.which('ninja')
|
||||||
|
if not ninja:
|
||||||
|
sys.exit('Ninja is required for the integration probe.')
|
||||||
|
target = probe_binary.name if probe_binary.parent == build else str(probe_binary)
|
||||||
|
subprocess.run([ninja, '-C', str(build), '-f', probe_manifest.name, target], check=True)
|
||||||
|
output = build / 'lazy-folios'
|
||||||
|
output.mkdir(exist_ok=True)
|
||||||
|
report = output / 'results.txt'
|
||||||
|
result = subprocess.run([str(probe_binary), str(fixture), str(report), str(output)], timeout=500)
|
||||||
|
if report.exists():
|
||||||
|
print(report.read_text(encoding='utf-8', errors='replace'))
|
||||||
|
sys.exit(result.returncode)
|
||||||
@@ -317,6 +317,169 @@ private slots:
|
|||||||
QCOMPARE(without_save_stamps(read(back)), without_save_stamps(read(straight)));
|
QCOMPARE(without_save_stamps(read(back)), without_save_stamps(read(straight)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QElectroTech run in a sandbox of its own with @p args, the store
|
||||||
|
// check on (QET_CHECK_ELEMENT_INFO_STORE); its output
|
||||||
|
QString runChecked(const QStringList &args, bool eager = false)
|
||||||
|
{
|
||||||
|
const QString home = m_dir.filePath(QStringLiteral("home%1").arg(m_run++));
|
||||||
|
QDir().mkpath(home);
|
||||||
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||||
|
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||||
|
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
||||||
|
env.insert(QStringLiteral("QET_CHECK_ELEMENT_INFO_STORE"), QStringLiteral("1"));
|
||||||
|
if (eager) {
|
||||||
|
env.remove(QStringLiteral("QET_LAZY_FOLIOS")); //every folio built on opening
|
||||||
|
}
|
||||||
|
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), args);
|
||||||
|
if (!proc.waitForFinished(180000)) return {};
|
||||||
|
return QString::fromUtf8(proc.readAllStandardOutput() + proc.readAllStandardError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The stores of symbol information and folio title blocks
|
||||||
|
// (DB-ACCESSORS-PLAN.md stages 4.1, 4.4) agree with every placed symbol
|
||||||
|
// and folio once a project is open: every example.
|
||||||
|
void elementInfoStoreAgrees_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("project");
|
||||||
|
const QDir examples(QStringLiteral(QET_EXAMPLES_DIR));
|
||||||
|
for (const QString &f : examples.entryList({QStringLiteral("*.qet")}, QDir::Files, QDir::Name))
|
||||||
|
QTest::newRow(f.toUtf8().constData()) << examples.filePath(f);
|
||||||
|
}
|
||||||
|
void elementInfoStoreAgrees()
|
||||||
|
{
|
||||||
|
QFETCH(QString, project);
|
||||||
|
const QString out = runChecked({QStringLiteral("--resave"), project,
|
||||||
|
m_dir.filePath(QStringLiteral("store%1.qet").arg(m_run))});
|
||||||
|
QVERIFY2(out.contains(QStringLiteral("element information store: 0 differ")), qPrintable(out.right(600)));
|
||||||
|
QVERIFY2(out.contains(QStringLiteral("folio title block store: 0 differ")), qPrintable(out.right(600)));
|
||||||
|
QVERIFY2(out.contains(QStringLiteral("wire properties store: 0 differ")), qPrintable(out.right(600)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The stores filled from the document before the folios are built
|
||||||
|
// (DB-ACCESSORS-PLAN.md stage 4.6) hold what building them gives, for a
|
||||||
|
// project saved by this version (every symbol, folio and wire carries
|
||||||
|
// a uuid): nothing differs, nothing is left unplaced.
|
||||||
|
void storesFilledBeforeBuilding()
|
||||||
|
{
|
||||||
|
const QString saved = resave(QStringLiteral(QET_EXAMPLES_DIR "/industrial.qet"));
|
||||||
|
QVERIFY2(!saved.isEmpty(), "--resave failed");
|
||||||
|
const QString out = runChecked({QStringLiteral("--resave"), saved,
|
||||||
|
m_dir.filePath(QStringLiteral("prefilled.qet"))}, true);
|
||||||
|
const QRegularExpression line(QStringLiteral(
|
||||||
|
"filled before building: symbols (\\d+) agree, 0 differ, 0 not placed; "
|
||||||
|
"folios (\\d+) agree, 0 differ, 0 not placed; wires (\\d+) agree, 0 differ, 0 not placed"));
|
||||||
|
const QRegularExpressionMatch m = line.match(out);
|
||||||
|
QVERIFY2(m.hasMatch(), qPrintable(out.right(600)));
|
||||||
|
QVERIFY(m.captured(1).toInt() > 600 && m.captured(2).toInt() == 50 && m.captured(3).toInt() > 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title block fields stamped on every folio go through the store, and
|
||||||
|
// the saved folios carry them (a folio saves what the store holds).
|
||||||
|
void folioTitleBlockStoreAfterSetTitleBlock()
|
||||||
|
{
|
||||||
|
const QString saved = m_dir.filePath(QStringLiteral("stamped.qet"));
|
||||||
|
const QString out = runChecked({QStringLiteral("--set-titleblock"),
|
||||||
|
QStringLiteral(QET_EXAMPLES_DIR "/industrial.qet"), saved,
|
||||||
|
QStringLiteral("author=Ann"), QStringLiteral("extrafield=Hello")});
|
||||||
|
QVERIFY2(out.contains(QStringLiteral("folio title block store: 0 differ")), qPrintable(out.right(600)));
|
||||||
|
const QByteArray xml = read(saved);
|
||||||
|
QCOMPARE(xml.count("<diagram "), xml.count("author=\"Ann\"") - 1);
|
||||||
|
QCOMPARE(xml.count("<diagram "), xml.count("name=\"extrafield\"") - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and after a copy of a symbol, which briefly shares its original's
|
||||||
|
// uuid, and an undo: the original kept no row once (duplicateElements
|
||||||
|
// is the copy the paste and folio duplication make).
|
||||||
|
void elementInfoStoreAfterCopyAndUndo()
|
||||||
|
{
|
||||||
|
#ifndef QET_HAS_SCRIPTING
|
||||||
|
QSKIP("needs --run: this QElectroTech is built without Qt Qml");
|
||||||
|
#endif
|
||||||
|
const QString script = m_dir.filePath(QStringLiteral("copy.js"));
|
||||||
|
QFile js(script);
|
||||||
|
QVERIFY(js.open(QIODevice::WriteOnly));
|
||||||
|
js.write(QStringLiteral(
|
||||||
|
"var f = 0; while (qet.elementUuids(f).length < 3) ++f;\n"
|
||||||
|
"var e = qet.elementUuids(f);\n"
|
||||||
|
"qet.log('COPY ' + qet.duplicateElements(f, [e[0], e[1]], f, 300, 300).length);\n"
|
||||||
|
"qet.log('DELETE ' + qet.deleteElement(f, e[2]) + ' UNDO ' + qet.undo());\n"
|
||||||
|
"qet.log('SAVED ' + qet.save('%1'));\n").arg(m_dir.filePath(QStringLiteral("copied.qet"))).toUtf8());
|
||||||
|
js.close();
|
||||||
|
const QString out = runChecked({QStringLiteral("--run"), script,
|
||||||
|
QStringLiteral(QET_EXAMPLES_DIR "/industrial.qet")});
|
||||||
|
QVERIFY2(out.contains(QStringLiteral("COPY 2")) && out.contains(QStringLiteral("UNDO true"))
|
||||||
|
&& out.contains(QStringLiteral("SAVED true")), qPrintable(out.right(600)));
|
||||||
|
QVERIFY2(out.contains(QStringLiteral("element information store: 0 differ")), qPrintable(out.right(600)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// After copies of linked symbols, of wired symbols, and the undo of a
|
||||||
|
// folio's removal, the project database's symbol, link, wire and
|
||||||
|
// terminal rows are those a fresh open of the saved project builds
|
||||||
|
// (F106, F107): a copy is placed while it still carries its original's
|
||||||
|
// uuid, and an undone folio removal put back its symbols but not their
|
||||||
|
// rows.
|
||||||
|
void symbolRowsAfterCopyAndFolioUndo()
|
||||||
|
{
|
||||||
|
#ifndef QET_HAS_SCRIPTING
|
||||||
|
QSKIP("needs --run: this QElectroTech is built without Qt Qml");
|
||||||
|
#endif
|
||||||
|
const QString rows = QStringLiteral(
|
||||||
|
"function rows() { return JSON.stringify(["
|
||||||
|
"qet.query('SELECT uuid, diagram_uuid, type FROM element ORDER BY uuid'),"
|
||||||
|
"qet.query('SELECT element_uuid, label, comment FROM element_info ORDER BY element_uuid'),"
|
||||||
|
"qet.query('SELECT element_uuid, linked_uuid FROM link ORDER BY element_uuid, linked_uuid'),"
|
||||||
|
"qet.query('SELECT uuid, terminal1_element_uuid, terminal2_element_uuid, text FROM conductor ORDER BY uuid'),"
|
||||||
|
"qet.query('SELECT uuid, element_uuid FROM terminal ORDER BY uuid, element_uuid')]); }\n");
|
||||||
|
const QString edit = m_dir.filePath(QStringLiteral("rows_edit.js")),
|
||||||
|
dump = m_dir.filePath(QStringLiteral("rows_dump.js")),
|
||||||
|
saved = m_dir.filePath(QStringLiteral("rows.qet"));
|
||||||
|
QFile js(edit);
|
||||||
|
QVERIFY(js.open(QIODevice::WriteOnly));
|
||||||
|
js.write((rows + QStringLiteral(
|
||||||
|
"var linked = -1;\n"
|
||||||
|
"for (var f = 0; f < qet.folioCount() && linked < 0; ++f)\n"
|
||||||
|
" qet.elementUuids(f).forEach(function (u) { if (qet.linkedElements(f, u).length) linked = f; });\n"
|
||||||
|
"qet.log('UNDONE ' + (qet.removeFolio(linked) && qet.undo()));\n"
|
||||||
|
"var copied = 0;\n"
|
||||||
|
"for (var f = 0; f < qet.folioCount() && copied < 3; ++f) {\n"
|
||||||
|
" var e = qet.elementUuids(f);\n"
|
||||||
|
" for (var i = 0; i < e.length && copied < 3; ++i)\n"
|
||||||
|
" if (qet.linkedElements(f, e[i]).length)\n"
|
||||||
|
" copied += qet.duplicateElements(f, [e[i]], f, 300, 300).length;\n"
|
||||||
|
"}\n"
|
||||||
|
"qet.log('COPIED ' + copied);\n"
|
||||||
|
"var wf = 0; while (qet.elementUuids(wf).length < 6) ++wf;\n"
|
||||||
|
"var wires = qet.conductorCount(wf), w = qet.elementUuids(wf);\n"
|
||||||
|
"qet.log('WIRED ' + qet.duplicateElements(wf, w.slice(0, 6), wf, 300, 300).length"
|
||||||
|
" + (qet.conductorCount(wf) > wires ? ' with wires' : ''));\n"
|
||||||
|
"qet.log('ROWS ' + rows());\n"
|
||||||
|
"qet.log('SAVED ' + qet.save('%1'));\n").arg(saved)).toUtf8());
|
||||||
|
js.close();
|
||||||
|
QFile js2(dump);
|
||||||
|
QVERIFY(js2.open(QIODevice::WriteOnly));
|
||||||
|
js2.write((rows + QStringLiteral("qet.log('ROWS ' + rows());\n")).toUtf8());
|
||||||
|
js2.close();
|
||||||
|
|
||||||
|
const QString edited = runChecked({QStringLiteral("--run"), edit,
|
||||||
|
QStringLiteral(QET_EXAMPLES_DIR "/industrial.qet")});
|
||||||
|
QVERIFY2(edited.contains(QStringLiteral("COPIED 3")) && edited.contains(QStringLiteral("WIRED 6 with wires"))
|
||||||
|
&& edited.contains(QStringLiteral("UNDONE true"))
|
||||||
|
&& edited.contains(QStringLiteral("SAVED true")), qPrintable(edited.right(600)));
|
||||||
|
const QString reopened = runChecked({QStringLiteral("--run"), dump, saved});
|
||||||
|
auto rowsOf = [](const QString &log) {
|
||||||
|
const int at = log.indexOf(QStringLiteral("ROWS "));
|
||||||
|
return at < 0 ? QString() : log.mid(at + 5, log.indexOf(QLatin1Char('\n'), at) - at - 5);
|
||||||
|
};
|
||||||
|
QVERIFY(!rowsOf(reopened).isEmpty());
|
||||||
|
QCOMPARE(rowsOf(edited), rowsOf(reopened));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// A title-block value that is a single space is kept through two saves
|
// A title-block value that is a single space is kept through two saves
|
||||||
// (#973), and a value with accents comes back as it went in.
|
// (#973), and a value with accents comes back as it went in.
|
||||||
void singleSpaceValueKept()
|
void singleSpaceValueKept()
|
||||||
|
|||||||
Reference in New Issue
Block a user