AutoNum: a lot of changes

Schemas now get saved with a UUID and do not have to be looked up according to the form. They can be renamed and it gets reflected in existing elements. The GUI for
editing them is now much easier, e.g. forms can be reshuffled. A formula can be applied. Many options now also for conductors and sheets.
This commit is contained in:
Andre Rummler
2026-10-03 17:53:27 +02:00
parent 389fd454da
commit d76f3528ae
52 changed files with 6040 additions and 382 deletions
+266 -7
View File
@@ -68,6 +68,8 @@
#include "../TerminalStrip/physicalterminal.h"
#include "../TerminalStrip/realterminal.h"
#include "../TerminalStrip/terminalstrip.h"
#include "../autoNum/autonumschemecommand.h"
#include "../autoNum/elementautonumschemecommand.h"
#include "../autoNum/assignvariables.h"
#include "../autoNum/numerotationcontext.h"
#include "../borderproperties.h"
@@ -609,6 +611,11 @@ bool QetScriptApi::setInfoKey(int folioIndex, const QString &elementUuid,
log(QStringLiteral("qet.%1: empty information key").arg(caller));
return false;
}
if (key == QETInformation::ELMT_FORMULA_ID) {
log(QStringLiteral("qet.%1: \"%2\" is internal, follow a numbering with numberElement()")
.arg(caller, key));
return false;
}
Element *element = findElement(folioIndex, elementUuid);
if (!element) return false;
@@ -616,6 +623,10 @@ bool QetScriptApi::setInfoKey(int folioIndex, const QString &elementUuid,
if (old_info.value(key).toString() == value) return true; // nothing to push
DiagramContext new_info = old_info;
new_info.addValue(key, value);
// A formula written by hand follows no numbering scheme
if (key == QETInformation::ELMT_FORMULA) {
new_info.remove(QETInformation::ELMT_FORMULA_ID);
}
auto *cmd = new ChangeElementInformationCommand(element, old_info, new_info);
m_project->undoStack()->push(cmd);
@@ -3110,9 +3121,108 @@ bool QetScriptApi::addAutoNum(const QString &kind, const QString &name, const QS
}
}
if (kind == QLatin1String("conductor")) m_project->addConductorAutoNum(name, context);
else if (kind == QLatin1String("element")) m_project->addElementAutoNum(name, context);
else m_project->addFolioAutoNum(name, context);
if (kind == QLatin1String("element")) {
// Through the undo stack, and an existing scheme is edited
// rather than replaced: the elements following it follow
// the new definition (see ElementAutoNumSchemeCommand)
ElementAutoNumSchemeCommand *cmd = nullptr;
if (m_project->elementAutoNum().contains(name)) {
const int frozen = ElementAutoNumSchemeCommand::editBlockedBy(m_project, name, context).size();
if (frozen) {
log(QStringLiteral("qet.addAutoNum: %1 element(s) with a frozen label follow '%2', "
"its formula cannot change").arg(frozen).arg(name));
return false;
}
if (const auto conflict = ElementAutoNumSchemeCommand::counterConflict(m_project, name, context)) {
log(QStringLiteral("qet.addAutoNum: the counter %1 of '%2' is at or below %3 number(s) in use "
"(up to %4); new elements will skip them")
.arg(conflict->counter).arg(name).arg(conflict->count).arg(conflict->highest));
}
cmd = ElementAutoNumSchemeCommand::edit(m_project, name, name, context, false);
} else {
const QString problem = ElementAutoNumSchemeCommand::nameProblem(m_project, name);
if (!problem.isEmpty()) {
log(QStringLiteral("qet.addAutoNum: %1").arg(problem));
return false;
}
cmd = ElementAutoNumSchemeCommand::create(m_project, name, context, QUuid(), false);
}
if (cmd) m_project->undoStack()->push(cmd);
return true;
}
// Conductor and folio numberings: undoable too, an existing one is
// edited, and a new name is unique and not empty
const auto scheme_kind = kind == QLatin1String("conductor")
? AutoNumSchemeCommand::Kind::Conductor
: AutoNumSchemeCommand::Kind::Folio;
AutoNumSchemeCommand *cmd = nullptr;
if (AutoNumSchemeCommand::contains(m_project, scheme_kind, name)) {
cmd = AutoNumSchemeCommand::edit(m_project, scheme_kind, name, name, context);
} else {
const QString problem = AutoNumSchemeCommand::nameProblem(m_project, scheme_kind, name);
if (!problem.isEmpty()) {
log(QStringLiteral("qet.addAutoNum: %1").arg(problem));
return false;
}
cmd = AutoNumSchemeCommand::create(m_project, scheme_kind, name, context);
}
if (cmd) m_project->undoStack()->push(cmd);
return true;
}
/**
@brief QetScriptApi::renameAutoNum
Rename a numbering context. What follows it follows it under its new
name: the elements for an element numbering, the folios for a conductor
or folio numbering. Undoable.
*/
bool QetScriptApi::renameAutoNum(const QString &kind, const QString &name, const QString &newName)
{
if (!m_project) return false;
if (m_project->isReadOnly()) {
log(QStringLiteral("qet.renameAutoNum: project is read-only"));
return false;
}
if (kind != QLatin1String("element") && kind != QLatin1String("conductor")
&& kind != QLatin1String("folio")) {
log(QStringLiteral("qet.renameAutoNum: unknown kind '%1'; expected conductor, element or folio").arg(kind));
return false;
}
if (kind != QLatin1String("element"))
{
// The folios which follow it follow it under its new name
const auto scheme_kind = kind == QLatin1String("conductor")
? AutoNumSchemeCommand::Kind::Conductor
: AutoNumSchemeCommand::Kind::Folio;
if (!AutoNumSchemeCommand::contains(m_project, scheme_kind, name)) {
log(QStringLiteral("qet.renameAutoNum: no %1 auto-numbering named '%2'").arg(kind, name));
return false;
}
const QString problem = AutoNumSchemeCommand::nameProblem(m_project, scheme_kind, newName, name);
if (!problem.isEmpty()) {
log(QStringLiteral("qet.renameAutoNum: %1").arg(problem));
return false;
}
auto *cmd = AutoNumSchemeCommand::edit(
m_project, scheme_kind, name, newName,
AutoNumSchemeCommand::contextOf(m_project, scheme_kind, name));
if (cmd) m_project->undoStack()->push(cmd);
return true;
}
if (!m_project->elementAutoNum().contains(name)) {
log(QStringLiteral("qet.renameAutoNum: no element auto-numbering named '%1'").arg(name));
return false;
}
const QString problem = ElementAutoNumSchemeCommand::nameProblem(m_project, newName, name);
if (!problem.isEmpty()) {
log(QStringLiteral("qet.renameAutoNum: %1").arg(problem));
return false;
}
auto *cmd = ElementAutoNumSchemeCommand::edit(
m_project, name, newName, m_project->elementAutoNum(name), false);
if (cmd) m_project->undoStack()->push(cmd);
return true;
}
@@ -3129,9 +3239,30 @@ bool QetScriptApi::removeAutoNum(const QString &kind, const QString &name)
log(QStringLiteral("qet.removeAutoNum: no %1 auto-numbering named '%2'").arg(kind, name));
return false;
}
if (kind == QLatin1String("conductor")) m_project->removeConductorAutoNum(name);
else if (kind == QLatin1String("element")) m_project->removeElementAutoNum(name);
else m_project->removeFolioAutoNum(name);
if (kind == QLatin1String("element")) {
const int used = m_project->elementsUsingElementAutoNum(name).size();
if (used) {
log(QStringLiteral("qet.removeAutoNum: %1 element(s) follow '%2', it cannot be removed")
.arg(used).arg(name));
return false;
}
if (auto *cmd = ElementAutoNumSchemeCommand::remove(m_project, name)) {
m_project->undoStack()->push(cmd);
}
return true;
}
const auto scheme_kind = kind == QLatin1String("conductor")
? AutoNumSchemeCommand::Kind::Conductor
: AutoNumSchemeCommand::Kind::Folio;
const int used = AutoNumSchemeCommand::usersOf(m_project, scheme_kind, name).size();
if (used) {
log(QStringLiteral("qet.removeAutoNum: %1 folio(s) follow '%2', it cannot be removed")
.arg(used).arg(name));
return false;
}
if (auto *cmd = AutoNumSchemeCommand::remove(m_project, scheme_kind, name)) {
m_project->undoStack()->push(cmd);
}
return true;
}
@@ -3732,7 +3863,8 @@ bool QetScriptApi::numberElement(int folioIndex, const QString &elementUuid)
element->setUpFormula(true);
const DiagramContext new_info = element->elementInformations();
if (new_info.value(QETInformation::ELMT_LABEL) == old_info.value(QETInformation::ELMT_LABEL)
&& new_info.value(QStringLiteral("formula")) == old_info.value(QStringLiteral("formula"))) {
&& new_info.value(QStringLiteral("formula")) == old_info.value(QStringLiteral("formula"))
&& new_info.value(QETInformation::ELMT_FORMULA_ID) == old_info.value(QETInformation::ELMT_FORMULA_ID)) {
stack->endMacro();
return false;
}
@@ -3742,6 +3874,129 @@ bool QetScriptApi::numberElement(int folioIndex, const QString &elementUuid)
return true;
}
/**
@brief QetScriptApi::renumberElementAutoNum
Number again, from the first number, the elements following the
element auto-numbering @p name, in folio and position order, as the
"Renumber" button of the project's numberings does. Undoable, one
step.
An element whose label is frozen is left as it is, and its label is
not given to another element.
@return how many elements were left as they are because their label
is frozen, -1 if there is no such numbering or the project is read-only
*/
int QetScriptApi::renumberElementAutoNum(const QString &name)
{
if (!m_project) return -1;
const QString caller = QStringLiteral("renumberElementAutoNum");
if (m_project->isReadOnly()) {
log(QStringLiteral("qet.%1: project is read-only").arg(caller));
return -1;
}
if (!m_project->elementAutoNum().contains(name)) {
log(QStringLiteral("qet.%1: no element auto-numbering named '%2'").arg(caller, name));
return -1;
}
QVector<Element *> frozen;
if (auto *cmd = ElementAutoNumSchemeCommand::renumber(
m_project, name, &frozen,
QObject::tr("Renuméroter les éléments (%1)").arg(name))) {
m_project->undoStack()->push(cmd);
}
return static_cast<int>(frozen.size());
}
/**
@brief QetScriptApi::freeElementNumbers
The numbers an element which follows an element auto-numbering may be
given by hand with assignElementNumber(): from 1 to a few past the
highest in use, those no other element of the numbering carries and which
would not give it the label of another element. Empty if the element
follows no auto-numbering, or one whose numbers are not a single sequence
(a folio number, a cycle or letters in the definition).
*/
QVariantList QetScriptApi::freeElementNumbers(int folioIndex, const QString &elementUuid)
{
QVariantList list;
if (!m_project) return list;
Element *element = findElement(folioIndex, elementUuid);
if (!element) return list;
const QString title = m_project->elementAutoNumTitle(
QUuid(element->elementInformations().value(QETInformation::ELMT_FORMULA_ID).toString()));
for (int n : ElementAutoNumSchemeCommand::freeNumbers(m_project, title, element)) {
list << n;
}
return list;
}
/**
@brief QetScriptApi::assignElementNumber
Give an element which follows an element auto-numbering the number
@p number of it, which must be free (see freeElementNumbers()): its label
is the formula worked out with that number and it keeps following the
numbering. If the counter of the numbering is at or below @p number it
moves past it. Undoable, one step.
*/
bool QetScriptApi::assignElementNumber(int folioIndex, const QString &elementUuid, int number)
{
if (!m_project) return false;
const QString caller = QStringLiteral("assignElementNumber");
if (m_project->isReadOnly()) {
log(QStringLiteral("qet.%1: project is read-only").arg(caller));
return false;
}
Element *element = findElement(folioIndex, elementUuid);
if (!element) return false;
QString problem;
auto *cmd = ElementAutoNumSchemeCommand::assignNumber(m_project, element, number, &problem);
if (!cmd) {
log(QStringLiteral("qet.%1: %2").arg(caller, problem));
return false;
}
m_project->undoStack()->push(cmd);
return true;
}
/**
@brief QetScriptApi::assignElementAutoNum
Make an element follow the element auto-numbering @p name: it gets
the formula and the next number of that numbering, which moves on.
The same command as picking the numbering in the element's
information window. Undoable, one step.
The element is left alone, and false returned, when it follows that
numbering already, is a slave or a report, or when something would be
lost: a frozen label, or a formula it follows or holds already, unless
@p overwrite is true. A label typed by hand, with no formula, is
replaced.
*/
bool QetScriptApi::assignElementAutoNum(const QString &name, int folioIndex,
const QString &elementUuid, bool overwrite)
{
if (!m_project) return false;
const QString caller = QStringLiteral("assignElementAutoNum");
if (m_project->isReadOnly()) {
log(QStringLiteral("qet.%1: project is read-only").arg(caller));
return false;
}
if (!m_project->elementAutoNum().contains(name)) {
log(QStringLiteral("qet.%1: no element auto-numbering named '%2'").arg(caller, name));
return false;
}
Element *element = findElement(folioIndex, elementUuid);
if (!element) return false;
auto *cmd = ElementAutoNumSchemeCommand::assign(m_project, name, {element}, overwrite);
if (!cmd) {
log(QStringLiteral("qet.%1: the element was left as it is").arg(caller));
return false;
}
m_project->undoStack()->push(cmd);
return true;
}
/**
@brief QetScriptApi::duplicateElements
Copy elements, and the conductors between them, to another place.
@@ -3751,6 +4006,10 @@ bool QetScriptApi::numberElement(int folioIndex, const QString &elementUuid)
elements are selected for the moment and the previous selection restored
before returning; the paste is Diagram::fromXml() at the position,
followed by one PasteDiagramCommand so it is a single undo step.
As for a paste in the editor, the copies of elements which follow an
element numbering get the next numbers of it (the preference "number
pasted elements"), and the numbering moves on; undoing gives them back.
@return the uuids of the new elements, or an empty list on failure
*/
QStringList QetScriptApi::duplicateElements(int fromFolioIndex, const QStringList &elementUuids,